Learn to Program with Ruby

You should learn to program. No, seriously. YOU should learn to program.

In 2024, it’s a cliche to say that technology is important. Software has already eaten the world. Most of our lives are inter-tangled with software applications, both via computers and smartphones.

We are surrounded by computers everywhere. We all know that programming is a critical skill to learn. Almost all industries have a digital footprint, and many companies are evolving to become “software” companies.

The importance of learning to program is greater than it has ever been.

We also know that software industry is one of the highest-paying industries. You can work from anywhere in the world and work any time you want. If done well, a career in programming and software development gives you the three important freedoms in life:

  1. Financial Freedom: Earn as much as you want
  2. Location Freedom: Work from anywhere in the world
  3. Time Freedom: Work whenever you want

But I guess you already knew all that. My goal is not to convince you about the financial or other material benefits of programming and software development.

I want to show you the other freedom of programming: The freedom to create; create whatever you want; build your own digital world and cultivate an online garden. Experience the joy of learning about computers and the magical world of software development.

When you learn to code, you learn to tell your computer how to do something. You can create pixels on the screen, and change those pixels to however you want, whatever you want. You can create the digital world you want.

Not only that, but your world-view also changes when you learn to program. You don’t consider computers as some complicated machinery, but just some devices that can be manipulated via programs.

So you should learn to program, to learn to create. Programming can be an extremely fun and rewarding hobby.

I can’t promise that you will immediately get a remote, six-figure job as a software developer. What I can promise is that your world will be different once you learn to program.

Learning to program is not hard.

Maybe you’ve heard that programming is hard, it’s full of complex Math, formulas, equations, calculations. 

Programming is not only about Math. Modern programming is much more than programming complex formulas. It’s about building websites, programming web applications, solving interesting problems, designing web pages, and much more.

Maybe you’ve even tried to learn to code when you were in college, and gave up, because the language you were learning was C or C++, or Java, and you just couldn’t figure it all out. The words pointers, polymorphism, inheritance, encapsulation were thrown onto you without explaining the need and the problems they were trying to solve, and it only added to the confusion.

Maybe someone told you that you weren’t fit to code, you weren’t smart enough to be a programmer, and you believed them.

They were wrong. It doesn’t have to be that way. Programming doesn’t have to be hard. It can be one of the most fulfilling and satisfying activities you can do.

Why Ruby?

One reason I am telling you that learning to program will be easier, because you will learn to program with me in one of the most elegant programming languages called Ruby. It’s one of the most beginner-friendly and one of the most beautiful programming languages in the world.

💡
Ruby is a programming language from Japan. It was created by Yukihiro “Matz” Matsumoto in 1995.

I’d go one step further and say that Ruby should be everyone’s first programming languages.

Don’t believe me? Let’s compare two programs that print the text “Hello World” on the screen.

Here’s the one in Java.

public class HelloWorld {
    public static void main(string []args) {
        System.out.println("Hello World")
    }
}

Here’s the same program in Ruby. Yes, it’s a complete program.

print "Hello World"

Not only the programs written in Ruby are wonderfully short, they are also much more expressive than most other programming languages. They're also free from most of the punctuation rules such as semi-colons or brackets and parenthesis.

Ruby is also an incredibly concise and readable programming language. What other languages take multiple statements to express, Ruby can express it in often a single statement. Even developers who have never seen Ruby can see a piece of code written in Ruby and understand what it does.

💡
You can read a piece of code written in Ruby like pure English and make sense of it, even if you haven’t programmed before.

Ruby is a fun programming language. What’s more, Matz, the creator of Ruby, created Ruby in the first place to make programmers happy.

Not the computer. Not the compiler. The developer.

Ruby is the only programming language focused on developer happiness. You will truly experience the joy of programming when using Ruby.

However, don’t think for a moment that Ruby is a “toy” programming language that’s fun to program but not build “serious” applications.

There’s nothing wrong with toys, toys are wonderful. But Ruby can be (and is) used to build some of the biggest applications and billion-dollar companies (like Shopify, GitHub, Airbnb, Zendesk) and multi-million dollar companies like 37signals, Cookpad, Intercom, and many more.

The web application framework Ruby on Rails is written in Ruby, which lets you create amazing web applications pretty quickly. All the above companies use Ruby on Rails.

No matter what anyone tells you, Ruby is the best programming language to start your programming journey.

Ruby also has a wonderful, friendly community all around the world. Although you write programs alone, having support when you need it is essential. There are hundreds of thousands of developers all over the world who are there to help you solve problems and support your journey as a developer.

So these are the reasons we will learn to program in Ruby. If you’ve never written a line of code before, Ruby is a wonderful choice as your first programming language.

Hope my words have convinced you enough to learn to program and program in Ruby. However, before you proceed, let me set clear expectations.

💥
This is not one of those “Learn to Program in 7 Days!!” post. You won’t finish reading this post and land a cushy, work-from-home tech job with a six-figure salary the next day.

Programming is a craft. Just like any craft, it takes years and years of patient practice and learning to master. However, getting started in programming is very easy. There are a handful of basic concepts you need to know to get started, and then you learn things as you go on. Technologies come and go, but the fundamentals of programming don’t change. And that’s what we’ll learn in this article. The fundamentals.

Learning to program is a long-term process. Anyone who tells you otherwise is trying to sell you their book/course/bootcamp. However, if you stick to the path and practice every day, you’ll be amazed at how much progress you can make in just a few months.

This post gives you the basics to start your programming journey.

Table of Contents

Sounds interesting? Let’s get you all set up for writing Ruby programs.


Day 1: Installation, Setup, and Our First Program

In this lesson, we will install all the tools you'll need for programming and write a simple program. Here's the software we'll install on our computer.

  1. iTerm: A Command Line or Terminal application to run the programs.
  2. Ruby: A programming language in which you write the programs
  3. Visual Studio Code: An Integrated Development Environment (IDE) to write the program.

Let’s get you set up with all three.

Step 1: Install Terminal (iTerm2)

So far, you’ve been using drag-and-drop applications, by pointing and clicking on icons. A terminal lets you type commands which let you accomplish anything. If you have never used a terminal before, think of it as a powerful way to interact with your computer.

Imagine you are a tourist in a foreign country. You don’t speak the language, but you can point to things and communicate with locals by making gestures.

Now you could probably get by like this for a while, and it’s enough if you’re just travelling or passing by. However, if you want to stay in that country for a long term, start a business, or be part of the community, you have to speak that language. Using the graphical applications and pointing and clicking at things is like the first way. Using a terminal is the second way.

To become a programmer, you must be familiar with a terminal.

At first, using a terminal feels slow, but as you get better, you will spend most of your time during development in terminal.

Your Mac comes with an integrated terminal out of the box. You can open it in one of two ways:

  • Click the Launchpad icon in the Dock, type Terminal in the search field, then click Terminal.
  • In the Finder, open the /Applications/Utilities folder, then double-click Terminal.

It should open a window like this:

However, we will be using a different terminal called iTerm2. It’s a powerful replacement for the default Terminal on Mac.

You can check out all the features here, but just trust me that your development experience will be much better with iTerm. I’ve been using if for the past many years, and love it.

Download and Install iTerm2

The ~ stands for “your home directory”. This is where you are: your home directory.

Let’s write your first command to print the text “hello world” on the terminal.

$ echo "hello world"
hello world

Here, echo is a command, and you are passing the text “hello world” to it. When you hit Enter, the terminal prints the text on the screen.

If you want to change to a different directory, you use the cd command, passing the name of the directory you want to switch to. cd stands for change directory. If you want to go back to the previous directory, type cd .. command

$ cd Desktop
$ Desktop
$ cd ..

That’s probably more than enough knowledge of the terminal to begin with. As we progress, we will learn more about the terminal.

Finally, as a bonus, also install Oh My Zsh.

Oh My Zsh is a delightful, open source, community-driven framework for managing your terminal configuration. It comes bundled with thousands of helpful functions, helpers, plugins, and themes.

If you are like me, you care about your work environment. You will spend a lot of time in your terminal, so it’s good to have the best possible toolset you can. Oh My Zsh just makes your experience very pleasurable.

Once you start using both iTerm2 and Oh My Zsh, you won’t feel like going back. Trust me.

Step 2: Install Ruby

To program in Ruby, we first have to install it on our computer.

If you’re on a Mac, it already comes with Ruby. However, it’s most likely an older version of Ruby. It’s also used by the system programs on your operating system so it’s best not to use it or mess with it. We will download the separate, latest version of Ruby.

There are many ways to install Ruby on your computer. The simplest way is to download Ruby is to use Homebrew, a package manager for macOS and Linux. Homebrew is a very useful tool as it lets you easily download various software packages. Later, we'll use Homebrew to install a database such as MySQL and other tools. So it’s best to download it right away.

Download Homebrew

Install Homebrew by copy + pasting the following script into your Terminal window.

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Note: Check out the website to understand what the above script does.

Once it finishes, test if it was successfully installed by running the following command:

$ brew --version
Homebrew 4.0.10

If it prints the version, it means Homebrew was successfully installed.

The next step is to install Ruby. Run the following command in the terminal:

$ brew install ruby

It takes a while, but once it's installed, you can run the following command to see if everything went OK.

$ ruby --version

If it prints the Ruby version you installed, you're good to go.

All right, you have installed all the necessary tools and are now all set and ready to program.

Step 3: Install Visual Studio Code

We will use VS Code for our IDE. In my opinion, it’s one of the best editors out there which is both powerful and lightweight at the same time.

Some of you might be fans of Sublime Text, so feel free to use it instead.

You can download and install VS Code here.

Next, we will create a new directory to save all our programs.

In the terminal, use the mkdir command to create a directory named code. Then, use the cd` command to switch into that directory.

$ mkdir code
$ cd code

Finally, open the VS Code, and open the code directory.

You can also launch VS Code directly from the terminal. See this article to learn how.

Now create a new file in this directory called main.rb. This is the file where our Ruby code will live.

To run simple programs, we will use the in-built terminal included in the VS Code. You can open and close it using the (Ctrl + `) keyboard shortcut.

Writing the First Program

Now that we have installed the necessary tools on the computer, the next step is to write a simple program and run it.

Let's write the first line of Ruby code that prints a greeting when executed.

print 'Hello World'

That's it. We're done. This is a complete Ruby program.

Nothing else is needed.

No require or include statements, no public static void main(), no semi-colons, no indentations, nothing.

Let's run it.

In the terminal window, navigate to your directory and run the following command.

$ ruby code.rb

Ruby will execute our program and print 'hello world' on the screen.

Amazing, right?

Congratulations, that was your first Ruby program. We are now ready to start programming.


Day 2: Variables, Expressions, and Statements

It's time to dive into various interesting features of Ruby. We'll start with the most basic and fundamental concept in programming: a variable. Then we'll explore the essential components that make our programs: expressions and statements. Finally, we'll wrap up by examining comments and operators.

What's in a Name?

Computers store all the information as bits, sequences of 1s and 0s. This is what computer’s memory looks like (actually, what the computer actually stores is electrical signals or charges, but let’s keep things simple for now).

... 1100101001010010010111101000001101101010 ....

However, to operate effectively with this information without getting bogged down into bits, programming languages operate on specific types of information, such as numbers or strings.

In real life, you assign names to things and people to identify them. Programming is not different. Instead of things and people, we have bits of memory, and we refer to it by something called a variable.

For example, when I say my name is Akshay, the term name is a variable that points to the value Akshay.

Why variable? because it's not fixed. A variable can point to different things during the lifetime of a program. To continue the previous example, someone else could say that their name is David, and the variable name is now pointing to the value David.

You can think of a variable is a placeholder. This placeholder refers to some value.

In the following example, we are storing the company name "Microsoft" inside a variable called company.

company = "Microsoft"

This is called defining a variable and assigning a value to it. The value on the right-side of the = (the word “Microsoft”) is assigned to the variable on the left-side (company). From now on, the variable company points to the word “Microsoft”, until you change its value.

The reason it's called a variable is because you can change its value, i.e. the value a variable points to can vary. Let's change the company name to "Apple". This is also called reassigning a variable.

company = "Apple"

You can also print variable's value, using the print command (it's a function, but we'll learn about functions and methods later) we saw earlier.

print company  # Apple

Now run the program again. Ruby will print 'Apple' on the terminal.

$ ruby code.rb
Apple

Let's change the name and print it again.

name = 'Microsoft'
print name  # Microsoft

Variables make our programs easier to read and write. For example, let’s print a greeting on the terminal:

puts "hello world"

Let’s say you want to print it again.

puts "hello world"

As you can see, it can get tedious if you have to do it again and again. Later, if you want to change the text of the greeting, say "hello world, how are you?", you have to find all the places where you’ve printed it and change the text.

It’s a lot easier if we can assign a name to this string and use that name instead. Then you only have to define the string once, and when you have to change it in future, you do it at once place.

greeting = "hello world"

puts greeting
# ...
puts greeting

Here’s a different example that combines a variable with another string.

name = "Akshay"
puts "My name is " + name + "."
puts "How are you, " + name + "?"

# Output
# My name is Akshay.
# How are you, Akshay?

In fact, there’s a better way to insert variables into strings, something called string interpolation. Simply put the variable inside #{} and Ruby will replace it with the original string when you run the code.

name = "Akshay"
puts "My name is #{name}."
puts "How are you, #{name}?"

# Output
# My name is Akshay.
# How are you, Akshay?

It makes the code much more readable. Here’s an example that shows both reassigning a variable and string interpolation together:

name = "Ruby"
puts "#{name} is a programming language."

name = "Ruby on Rails"
puts "#{name} is a web application development framework"

# Ruby is a programming language.
# Ruby on Rails is a web application development framework

You can also assign a number to a variable.

age = 30           # 30
amount = 1000      # 1000
new_age = age + 1  # 31

You are allowed to change a variable's value by reassigning it.

amount = 2000

In Ruby, variable names are case-sensitive. Hence name is different from Name. The standard convention is to use all lower-case words for variable name, such as age, name, amount, etc. and it can be almost any sequence of letters and numbers as long as it’s readable.

💡
To write readable and maintainable code, give your variables an intention-revealing name, i.e. the variable’s name should tell you what it is.

If the variable contains multiple words, separate them with an underscore _, such as student_name or employee_count. This naming convention is also known as snake_case.

So far, we’ve seen how to create variables and assign values to them, and even reassign them to different values. Can you assign a variable to another variable? Yes, you can!

# create a variable called `language` 
# that points to the value "Ruby"
language = "Ruby"

# create another variable called `programming_language`
# that points to the variable `language`
programming_language = language

puts language               # "Ruby"
puts programming_language   # "Ruby"

# reassign the variable `programming_language` 
# to a new value "Java"
programming_language = "Java"

puts language               # "Ruby"
puts programming_language   # "Java"

So far, we've been writing lines of Ruby code in the IDE or in the terminal. Each line is called a statement. A statement can contain one or more expressions. The difference might seem trivial, but it's important to understand.

Expressions

An expression is simply an operation in Ruby that returns a value. In other words, an expression can be evaluated.

For example, 5 * 4 is an expression that returns 20.

A function or method call (which we'll learn later), such as Math.min(10, 5) is also an expression that returns 5. In general, you don't use an expression in isolation, unless you're checking its value in the REPL.

Expressions are combined to form statements, which we'll learn next.

Statements

Combining one or more expressions with operators (+, -, etc.) and possibly assigning the result to a variable results in a statement. For example, result = 5 + 10 is a statement, so is print result.

A statement stands on its own. Executing a statement of Ruby code results in some side-effect, such as a variable getting initialized, a method being called, etc.

In addition to declaring variables, other statement types include conditional branches and loops.

Should you insert a semicolon or not?

If you are coming from more traditional programming languages like C# or Java, you may be used to inserting semicolons at the end of the statements to indicate the end of the statement, just like a period (.) indicates the end in regular English sentences.

However, in Ruby, the semicolon is optional.

Comments

Sometimes, you want to add helpful notes and instructions to your code for others, even you. Trust me, when you come back to a piece of code you wrote six months ago, it's often hard to figure out what it does.

Comments are a useful feature in all programming language that let you write plain English text which is not executed when the program runs.

A comment allows you to add notes and annotations to your program. You can use comments to explain what a program is doing and why.

Typically, the why is more important than what, which is self-evident.

In the following example, we add a comment: create and print a variable.

# create and print a variable
name = "Akshay"
print name

Note that it starts with #, which tells Ruby that it's a comment and do not execute it.

If you want to write multi-line comments, start each line with a #.

# This is a multi-line comment, 
# which spans multiple lines. 
print 'hello'

Operators

Expressions are generally combined with operators. For example, + is an operator that adds two numbers or joins two strings. Ruby supports all the operators you learned in high-school Maths, and many more.

A statement can have multiple operators operating on multiple values (also called operands). The order in which Ruby evaluates these operators is called "operator precedence". Operators with higher precedence are performed before those with lower precedence.

For example, consider the following statement:

a = 10
b = 5
c = 2

result_one = a + b * c     # 20
result_two = (a + b) * c   # 30

The multiplication operator has higher precedence than the addition operator. Hence, to evaluate result_one, Ruby will first multiply b and c and add a to the result, resulting in the answer 20.

In contrast, the parenthesis has higher precedence than the multiplication. So Ruby will add a and b and then multiply the result with c, giving us the value 30 for result_two.

Some operators act on two operands, making them binary operators, such as a + b.

Some act on a single operand, which makes them a unary operator, e.g. -5.

There is only one ternary operator in Ruby, ?:, which acts on three operands. We will explore it in the lesson on conditions.

Let's start with the simplest, and add two numbers, using the plus + operator.

first_number = 10
second_number = 20

sum = first_number + second_number
print sum  # prints 30

Nothing fancy here.

💡
Actually, there is. Since Ruby is a pure object-oriented language, + is actually a method defined on all numbers, which adds the argument, i.e. second number and returns the result. But ignore that for now. We may come back to it later.

Similarly, Ruby supports -, *, and / operators, which work as you expect. In addition, the = operator we've been using so far assigns the value on the right hand side to the variable on the left.


Day 3: Data Types - Strings, Numbers, and Symbols

Each variable has a specific type, which determines the blueprint and the shape of that value as well as what operations you can perform on it. Every value in Ruby can be one of the following five types:

Number

Most programs use numbers for keeping track of number of items, or for performing arithmetic calculations, or financial transactions. For this, all programming languages support a data type called a number.

In the text editor, type the following code:

puts 10 + 20

Save the file, open the terminal, and run the above program with the following command:

$ ruby code.rb
30

As expected, it prints the output : 30

We just performed a mathematical operation on two numbers. Nothing fancy here. Ruby supports numbers, and you can perform any standard arithmetic operation on them, just as you’d in real world maths.

# `age` is a variable of type Number
age = 30 

# let's add 1 to the age,
# and store it into `new_age`,
# another variable of type Number
new_age = age + 1

Here’re a few examples of numbers: 1, 2, 9, 3.14, 5.0, 25

We can use two different kinds of numbers in our programs.

  1. Integer Numbers: Numbers without a decimal point, e.g. 1, 2, 3, 99, -5, 0
  2. Floating-point Numbers: Numbers with a decimal point, e.g. 4.0, 3.14, 22.3, -7.3

String

💡
A string is simply a list of characters, for example 'hello world' and 'abcd'. The characters don't necessarily have to mean anything. In real programs, you may use strings to insert and display text on the web page. Open any website. Whatever text you see there, it's all made up of strings.

A string is a sequence of letters (or characters), words, or sentences. Here’re a few examples:

"hello world"
'how are you'
"a"
" "
", & -"
'4'
"3.14"

In Ruby, you represent a string by wrapping the characters inside double quotes " , single quotes ', or back-ticks (`). Anything can go inside the quotes, including letters, characters, punctuations, even numbers and spaces.

You can assign strings to variables, a concept we’ll examine later.

language = "Ruby"
framework = "Ruby on Rails"
company = 'Microsoft'
statement = "A company called #{company}"  # A company called Microsoft

As the last example shows, we can insert one string into another string by wrapping it between curly braces, preceded with a hash # character. This is called string interpolation and it's a pretty powerful and equally useful feature of Ruby.

The following example should make it clear.

name = 'Akshay'
greeting = "Hello #{name}, how are you?"

print greeting  # Hello Akshay, how are you?

Notice the special syntax around name on line 2: #{name}. When Ruby executes this line, it inserts the value of the name variable into the string to generate the resulting string.

💡
You can use either single-quote (') or double-quote (") to declare strings. A big difference between the two is that the double-quotes let you insert a variable's value inside the string.

You can print a string on the terminal using the puts and print methods. The only difference between them is that puts inserts a new line at the end of the string.

puts "hello"
print "world"

To add (or join) two strings together, you can use the + operator:

greeting = "hello" + " world" # hello world

puts "how" + " are you?"  # how are you?

Here, we joined two strings "hello" and "world" together and assigned the result "hello world" to the "greeting" variable.

What can you do with strings?

Strings are not simply a plain list of characters. You can perform various operations on them, such as calculate its length, convert the string to uppercase or lowercase, or combine multiple strings, and much more.

language = 'Ruby'
framework = 'Rails'

# check length of the string
print framework.length  # 5

# change case
print language.upcase  # RUBY
print language.downcase  # ruby

# combine strings
sentence = language + framework  # RubyRails

For a complete list of all the methods on strings, check out the String class. But you don't have to memorize them all, as you read and write more and more Ruby programs, you'll gradually become familiar with them.

Boolean

A variable of type boolean has only two values: true and false.

Booleans are used to evaluate conditions and take different actions based on the condition. If the condition is true, do something. If it's false, do something else.

The purpose of conditions will be clear when we learn conditions.

Example: true or false

is_draft = false
published = true

nil

nil is a special value reserved in Ruby to indicate an absence of value. In conditions, nil evaluates to falsey.

Symbol

A symbol is a value similar to a string, but it's guaranteed to be unique. If that's confusing, don't worry.

Symbols are simply strings that are guaranteed to be unique. Instead of quotes, symbols are indicated by adding a colon : character behind the word. For example, :gmail is a symbol. If you create another :gmail it will represent the same symbol you created earlier.

A symbol looks like a variable, but it's not. Instead, it's much closer to a string. So what's the difference between the string 'gmail' and the symbol :gmail, you ask?

When you create two strings, Ruby creates two separate objects behind the scenes. If you aren't familiar with the concept of objects, don't worry. It simply represents a bunch of memory addresses on your computer. Each object gets its own unique memory.

You can verify that the objects are different by printing their object_id. Type the following code in your Ruby script and run it.

puts 'gmail'.object_id  # 13900

puts 'gmail'.object_id  # 16660

puts :gmail.object_id  # 2385628

puts :gmail.object_id  # 2385628

Notice the first two strings printed two different numbers, indicating two separate objects. However, the symbols represent the same object.

The puts command works like print, the only difference is that it adds a new line after printing the provided text. So each number is printed on a separate line. Change it to print and see for yourself.

A nice benefit of using symbols is saving memory. If you create 1000 strings, Ruby will create 1000 string objects, each occupying different blocks of memory. In contrast, 1000 symbols will represent the same block of memory.

Most of the time, you'll use symbols in your Ruby programs as identifiers, i.e. to identify unique things. Don't worry if that doesn't make any sense. It will become clear as time passes.


Day 4: Making Decisions with Conditions

Whenever we want to take certain action based on the outcome of some expression is true or false, we use a condition.

Conditions are a part of control flow. Whenever we make a decision, we're using a conditional. We can decide to do something, or something else, based on a condition.

If this, then that.

puts "Enter your age"
age = gets.chomp

if (age > 15)
  puts "you can drive"
end

# Enter your age
# 18
# you can drive

The code within the if..end block executes only when the condition provided to the if statement evaluates to true.

Few things to note here:

  • The condition takes the form of if (condition) followed by a block of code, where condition is a boolean value or an expression that results in a boolean.
  • The condition must be surrounded by parentheses. In Ruby, the parentheses are optional.
  • The code that needs to be executed when the condition is true must be provided as a block of code if it's more than one lines long.

If you want to execute only a single statement when the condition is true, it can be on the same line, without the need of curly braces.

You could write the above example as follows:

print 'you can drive' if (age > 15)

Often, you want a program to do something when the condition is true and do something else if the condition is false. In these cases, use the else statement with if.

puts "Enter your age"
age = gets.chomp

if (age > 15)
  puts 'you can drive a car'
else
  puts 'you can ride a bicycle'
end

If you have more than one conditions, chain them using elsif as follows:

age = 15

if (age > 15)
  print 'you can drive a car'
elsif (age > 5)
  print 'you can ride a bicycle'
else
  print 'you can ride a tricycle'
end

When you have too many conditionals, it's better to use a case..when statement.

age = 18

case age
when 0..5
  print 'tricycle'
when 6..15
  print 'bicycle'
else
  print 'car'
end

You might be wondering what's the 0..5 syntax. It's called a Range. It indicates the range from the number 0 to the number 5, including both. We'll learn about it in a future chapter.

To fully understand the power of Ruby's case statement, check out the following article: Ruby's Switch Statement is More Flexible Than You Thought

Ruby’s Switch Statement is More Flexible Than You Thought
Ruby’s switch statement is very versatile and flexible, especially due to the dynamic nature of Ruby. In this post, we’ll see how you can use it in various ways. We’ll also learn why it works the way it works. Hint: it uses the `===` operator (method) under the hood.

What is considered truthy and falsy in Ruby?

So far, we have seen the boolean type values true and false. In Ruby, nil is also considered falsy value.

can_drive = nil

if (can_drive)
  print "you can drive"
else 
  print "wait for one more year"
end

# wait for one more year

Everything else is considered truthy.

can_drive = "YES"

if (can_drive)
  print "you can drive"
else 
  print "wait for one more year"
end

# you can drive

To create the conditions used in the conditional statements, you use comparison operators. In the next section, we’ll learn the comparison operators available in Ruby.

Comparison Operators

You can compare two numbers using the less-than (<), greater-than (>), less-than-or-equal-to (<=), and greater-than-or-equal-to (>=) operators.

All these comparison operators return boolean values true and false.

puts 3 < 5 # true
puts 5 > 3 # true

num = 7
puts num <= 7 # true
puts num >= 7 # true

To compare if two variables are equal or not, use the == and != operators.

a = 10
b = 10
c = 12

a == b  # true
b == c  # false
a != c  # true

Note: Don’t confuse the assignment = operator with equality == operator. The first one assigns a value to a variable, whereas the second one checks if the two values are equal or not.

So far, we’ve been comparing numbers. You may be surprised to find out that strings can be compared, too. Ruby compares two strings using the lexicographical order, i.e. how they appear in a dictionary. In addition, uppercase letters come before lowercase ones.

puts "Hello" < "hello"  # true
puts "greeting" < "hello" # true 

To prevent any unwanted surprises, it’s better to make both strings same case, using either upcase or downcase method.

puts "cat".downcase < "Dog".downcase  # true

Logical Operators

So far, we’ve learned how to take (or not take) a certain action, i.e. execute a block of code if a condition is true. However, in real life, there’re often multiple criteria that need to be evaluated for us to take some action. For example:

  • a person can only drive if they are above 15 or they’re accompanied by an adult
  • publish the post only if it’s undergone at least three revisions and is reviewed by an editor
  • start the program if it’s not already running.

Notice the highlighted words: or, and, and not. They either combine multiple conditions or revert the original condition.

How would you represent this in a program?

To combine multiple conditions, Ruby provides logical operators: or, and and not. Talk about Ruby being readable! They also have their equivalent mathematical operators ||, && and ! which you might have seen if you’re coming to Ruby from another, more traditional programming language. For most situations, you can use either of them.

Let’s represent the above three real-world examples in programs:

  • a person can only drive if they are above 15 or they’re accompanied by an adult
puts "enter your age:"
age = gets.chomp
puts "are you accompanied by an adult?"
accompanied_by_adult = gets.chomp

if (age > 15 || accompanied_by_adult)
  puts "you can drive the car"
else
  puts "here's the bicycle"
end
  • publish the post only if it’s undergone at least three revisions and is reviewed by an editor
puts "number of revisions:"
revision_count = gets.chomp
puts "is reviewed by an editor? enter 'yes' or 'no'"
reviewed_by_editor = gets.chomp

if (revision_count >= 3 && reviewed_by_editor == "yes")
  puts "publishing the post"
end
  • start the program if it’s not already running.
puts "is program running? enter 'yes' or 'no'"
is_running = gets.chomp
if !is_running
  puts "start the program"
end

To make the last example even more readable, Ruby provides the unless statement, which works in exact opposite of the if statement.

unless is_running
  puts "start the program"
end

# OR

puts "start the program" unless is_running

Logical Operators

Ruby has two specific values true and false which indicate whether an expression is true or false. I understand it might not make sense, but trust me that it will, once we see a few examples.

Most likely, you'll be using &&, || and ! operators with boolean values. This is how they work.

# Logical AND

true && true; # true
true && false; # false
false && true; # false
false && false; # false

# Logical OR

true || true; # true
true || false # true
false || true # true
false || false # false

# NOT
!true # false
!false # true

You can use any boolean expressions (expressions returning true and false) in place of true and false, and it works the same.

result = 5

(result > 3) && (result < 7) # true
(result == 0) || (result > 8) # false

Day 5: Repeating Code with Loops

Often in life, you want do certain action again and again. For example, you may want to wish everyone “Happy New Year!” until it’s January 2nd. This is equally true in programming, where you want to repeat a statement or a block of code as long as a condition is true. Computers are excellent at repeating instructions without getting tired or without forgetting.

A loop is another fundamental control structure that allows you to repeat (or loop) a block of code a certain number of times, or until a certain condition is met. You can also have an infinite loop.

Here's the simplest example: Print something 5 times.

5.times do
  print 'a'  # aaaaa
end

A more common pattern is to repeat an action for all the elements of an array.

names = ['Taylor', 'David']

names.each do |name|
  puts "Hello, #{name}"
end

# Output
#
# Hello, Taylor
# Hello, David

If you want to repeat something while some condition is true, use the while loop.

number = 0

while (number < 10)
  print number
  number += 1
end

# Output
0123456789

While Loop

The while loop takes a boolean expression, and executes a statement (or a block of code with multiple statements) while that condition evaluates to true.

The idea is that the code inside the block will cause a side-effect that will evaluate the boolean condition to false in a future iteration, which will stop the loop.

For example, let's say we want to keep buying some stock until its price reaches $100, at which we sell the stock. This can be represented programmatically using the while loop as follows:

price = 10

while price < 100
  puts "Buying the stock at #{price} dollars"
  price += 20
end

puts "Selling the stock"

## Output

Buying the stock at 10 dollars
Buying the stock at 30 dollars
Buying the stock at 50 dollars
Buying the stock at 70 dollars
Buying the stock at 90 dollars
Selling the stock

It's important to remember that the condition is evaluated first. That means the control will never enter the block if the condition evaluates to false. For example, if the price was greater than 100 to begin with, the block would not have been executed.

Infinite Loop

If you want to perform an action infinitely, there are multiple ways to go about it. The simplest option is to use the loop method.

loop do
  puts "enter your name"
  name = gets.chomp
  puts "welcome to Ruby, #{name}"
end

You can exit out of this loop by pressing ctrl + c.

You can make the while loop by providing the true condition.

while true
  # do something
end

Often you might accidentally enter an infinite loop by making a mistake in the condition such that it never evaluates to false. Be careful.

So far, we’ve learn how to repeat something. However, you also need to know how to get out of the loop and skip the rest of the code before moving to the next iteration. For this, Ruby provides two special keywords: break and next.

Break and Next

Often while a loop is running, you want to stop the computation and get out of the loop after a condition is met. Use the break statement for this.

num = 0

while num < 20
  print num
  break if num == 7
  num += 1
end

# Output
#
# 01234567

In contrast, sometimes you want to skip the rest of the computation inside the loop when a condition is met, and continue from the next iteration. Use the continue statement for this.

characters = ['a', 'b', 'c']
characters.each do |char|
  next if char == 'b'
  print char
end

# Output
# 
# ac

Day 6: Making a List with an Array

💡
Arrays are ordered lists of values grouped under a common name.

Arrays represent a list of elements. They are very common in programming and are one of the fundamental building blocks of programs. In this article, we'll learn how they work in Ruby.

So far we have seen variables which store a single entity, e.g. name = 'Akshay' and such. What if you want to store multiple names?

Imagine you want to save the names of 3 students in a class. One approach is to store each name in a separate variable, like this:

student_one = 'Mark'
student_two = 'Steve'
student_three = 'Sean'

A different, and better approach is to use a list of names, also known as an Array. When using arrays, you create a single variable that stores a list of data you want to store. Because an array typically contains more than one element, it’s a good idea to make the variable name plural, i.e. names, people, products, etc.

Creating an Array

In Ruby (and in most programming languages) you create a list using square brackets [], with individual elements separated by comma.

names = [ 'Mark', 'Steve', 'Sean']

There are multiple ways to create arrays in Ruby.

numbers = [1, 2, 3, 4]

grades = ['a', 'b', 'c']

# You can create a new empty array as follows:
list = Array.new()  # []
names = []

If you're coming from a language like C# or Java, it might surprise you that a Ruby array can hold values of any type, since Ruby is a dynamic language.

# an array containing a character, a number, and a symbol
values = [ 'a', 10, :rails ]
list = ['a', 3, 'word', 7]

However, it's not very common practice. In practice, arrays will hold values of the same type.

An array can even hold other arrays.

coordinates = [4, 7, [3, 5]]

Accessing Array Elements

Now you might be wondering how you can access the name of the third student. For this, we use an array index, which is just a number to refer to the position of that item. In most programming languages, array indices start from 0. So the first element has an index of 0, second has 1, and so on.

Using zero-based index, you can access any element from the array by subtracting 1 from its position in the array. In our case, the third student has an index of 2. Once we have the index i and the array name, we'll use the array[i] notation to refer to the item at that index.

To access the first and third names in the array, we'll use the indices 0 and 2, respectively:

items = ["book", "coffee", "desk", "cart"]

print items[0]  # book
print items[2]  # desk

Alternatively, Ruby provides the at method that takes the index and returns the element at that index.

print items.at(1)   # coffee

Ruby provides a special syntax (or index) to access the last element. The item at index -1 is always the last element, -2 is the second-last, and so on.

print items[-1]  # cart
print items[-2]  # desk

Ruby provides conveniently named first and last methods to access the first and last elements of the array.

numbers = [53, 75, 67, 95]

print numbers.first  # 53
print numbers[0]     # 53
print numbers[1]     # 75
print numbers[2]     # 67
print numbers[3]     # 95
print numbers.last   # 95

If you have an array and want to find its index in the array, use the find_index or index method on the array.

numbers = [53, 75, 67, 95]

print numbers.index(67)  # 2

Modifying an Array

In contrast to traditional language like Java and C#, arrays in Ruby are dynamic in size, meaning the size of an array can grow and shrink as needed. You can add and remove elements from the array as needed and Ruby takes care of the rest.

To add new elements at the end of an existing array, use the << notation.

numbers = [53, 75, 67, 95]
numbers << 83
print numbers  # [53, 75, 67, 95, 83]

To add it at the front, use the unshift method.

numbers = [53, 75, 67, 95]
numbers.unshift(10)
print numbers  # [10, 53, 75, 67, 95, 83]

To remove the last element from an array, use the pop method.

numbers = [53, 75, 67, 95]
numbers.pop    # 95
print numbers  # [53, 75, 67]

To remove the first element, use the shift method.

numbers = [53, 75, 67, 95]
numbers.shift  # 53
print numbers  # [75, 67, 95]

To remove an element at a given index, use the delete_at method.

numbers = [53, 75, 67, 95]
numbers.delete_at(1) # 75
print numbers        # [53, 67, 95]

Array Operations / Methods

To count how many items there are in an array, use the length, count, or size method.

languages = ['Ruby', 'PHP', 'Java', 'CSharp', 'Go']

print languages.length  # 5
print languages.count  # 5
print languages.size  # 5

To check if an array contains an item or not, use the include? method.

languages = ['Ruby', 'PHP', 'Java', 'CSharp', 'Go']

languages.include? 'Python'  # false
languages.include? 'Go'  # true

To find the position of an element in an array, use the index method. If that element does not exist in the array, Ruby returns nil value.

languages = ['Ruby', 'PHP', 'Java', 'CSharp', 'Go']

languages.index 'PHP'  # 1
languages.index 'Python'  # nil

In addition to these methods, there are many other useful methods you can use on the Array. For a complete reference, check out the Array documentation.

To summarize what we’ve learned so far, an array is an ordered collection of values, preferably (but not necessarily) of the same time. It is a very fundamental data structure in programming. Each element in the array has a number associated with it, also called the index of that element. Indices start with 0 and you access the element at a particular position using the square bracket notation, like arr[2] returns the third element in the array. Arrays can grow or shrink as needed.


Day 7: Mapping a Key-Value Pair with a Hash

In this lesson, we’ll learn about Ruby’s hash data structure, which lets you store related pieces of data as a collection of key-value pairs. Each key is connected to a value, and you use the key to access the corresponding value. We’ll learn how to create a hash, add data to it as well as remove data from it, and read data from it.

A hash is a very powerful and flexible data structure in programming. It’s also known as a dictionary or a map in other programming languages. Understanding how hashes work lets you model almost any real-world data in programming.

A Hash is similar to an Array, but instead of a list of individual elements, it stores a list of key-value pairs. In other words,

  • An Array index is always a number.
  • A Hash key can be (almost) any object.

Most likely, a Hash key will be a symbol or a string.

Just like in any programming concept, understanding and using a Hash will take patience and practice. However, since Ruby is so readable and concise language, you’ll quickly learn to appreciate the full power of Ruby’s hashes to model most real-world situations.

A Simple Hash

Here’s a simple hash that stores two pieces of information: name and price of a product.

product = { name: "iPhone", price: 450 }

Here, we created a hash with two keys :name and :price with values “iPhone” and 450 respectively, and assigned that hash to a variable named product. Note that the keys are symbols.

Keys are connected to values by colons and the key-value pairs are separated by commas. There’s no limit on the number of pairs you can store in the hash (only physical limit is the size of the memory on your machine).

In fact, there are two ways to create a Hash. The version we just saw is the modern version, which can only be used when the Hash keys are symbols. The older version is bit more verbose, but allows you to use any data type for keys, as long as there’re no duplicate keys.

# older version
product = { "name" => "iPhone", "price" => 450 }

In the above example, we used string keys: "``name``" and "``price``".

A hash’s key and value can be of any data type: number, string, array, objects, and even another hash. However, the best practice is to use either strings or symbols for keys.

The simplest hash is an empty hash.

product = {}

The above hash has no keys and no values.

Accessing a Hash

You can access the Hash values like an Array. The only difference is that you've to pass the key, instead of an index. When you provide the key in square brackets, the hash will return the associated value for that key.

product = { name: "iPhone", price: 450 }
product[:name]    # iPhone
product[:price]   # Rails

product = { "name" => "iPhone", "price" => 450 }
product["name"]   # iPhone
product["price"]  # 450

However, sometimes a key might not exist in a hash. In those cases, the hash simply returns nil value, which might not be what you’re expecting.

product = { name: "iPhone", price: 450 }
product[:company]  # nil

If you want Ruby to throw an error for missing keys, use the fetch method on the hash.

product.fetch(:company)  # key not found: :company (KeyError)

The fetch method takes the key as the first argument. Optionally, you can pass a second argument that will be returns if the key is missing. You can use it for default values or even an error message.

product.fetch(:company, "Apple")        # Apple
product.fetch(:company, "Missing key")  # Missing key

To check if a Hash contains a key, use the include? method. It's also aliased as has_key?, key?, and member?.

product = { name: "iPhone", price: 450 }

product.include? 'name"  # true
product.key? "creator"   # false

To get all the keys and values as an Array, use the aptly named keys and values methods on the Hash.

product = { name: "iPhone", price: 450 }

product.keys    # ["name", "price"]
product.values  # ["iPhone", 450]
In addition to these methods, there are many other useful methods you can use on the Array. For a complete reference, check out the Hash documentation.

Inserting New Data into a Hash

Just like arrays, hashes in Ruby are not fixed in size. They can grow and shrink as needed during the lifecycle of your programs. You can add new key-value pairs and remove existing ones from the hash at any time.

Let’s add a new key-value pair to the product hash. You do this by passing the new key in square brackets to the hash and assigning a value to it.

product = { name: "iPhone", price: 450 }
product[:company] = "Apple"

puts product  # { name: "iPhone", price: 450, company: "Apple" }

You can even start with an empty hash and then add new keys with values to it.

Keep in mind: Hashes in Ruby retain the order in which the keys were defined and the keys which were added later. This might not seem like a big deal, but it’s important when you are looping over the keys or values, or even the key-value pairs.

Removing Data from a Hash

If you want to remove a key-value pair from a hash, use the delete method on the hash.

Let’s say you want to remove the location from the product.

product = { name: "iPhone", price: 450, location: "Cupertino" }

product.delete(:location)  # Cupertino

puts product  # { name: "iPhone", price: 450 }

Note that the delete method returns the value of the key you just removed, i.e. Cupertino.

Modifying a Hash

Just like you can change the values in an array, you can change them in a hash. To modify a value for a given key in Ruby, pass the name of the key to the hash in square brackets and assign a new value to it.

product = { name: "iPhone", price: 450 }
puts product[:price]  # 450

product[:price] = 575
puts product[:price]  # 575

From now on, the new price of the product will be 575.

If you want to replace existing key name with a different key, but the same value, simply delete the old key and assign its value to the new key:

product = { name: "iPhone", price: 450 }
product[:cost] = product.delete(:price)

puts product  # { name: "iPhone", cost: 450 }

In this chapter, we learned the basics of Ruby’s hashes. We saw how to create a hash, how to add and remove data from it, and even update existing data from it.


Day 8: Don't Repeat Yourself with Functions

💡
Functions are named blocks of code designed for a specific task.

So far, we have seen expressions and statements in Ruby. What if we want to execute a bunch of related statements together, multiple times? In this lesson, we will learn to write functions, which let you accomplish this.

A function is one of the most important concepts in programming. You can use a function to group together multiple instructions, multiple lines of code, and give them a name.

In essence, functions are named blocks of code designed to do one specific job. When you want to perform this job, you call the specific function that’s responsible for this job. If you want to perform the job repeatedly, you call the function multiple times.

💡
Using functions makes your programs easier to read, write, test, and debug.

You might have heard the term DRY, which stands for Don’t Repeat Yourself. It’s one of the core goals in programming, and functions let us achieve it, by allowing you to write code once and reusing it as many times as you want.

For example, imagine we want to send an email to a user by getting their email. You can write this code as follows:

to_email = "user@mail.com"
from_email = "me@email.com"
subject = "Hello"

puts "sending email to #{to_email}"

If, at some other point in time, you want to send another email, you might duplicate the above four lines.

# somewhere else...

to_email = "user@mail.com"
from_email = "me@email.com"
subject = "Hello"

puts "sending email to #{to_email}"

As you can see, it can get really tedious to duplicate the same code again and again. It's worse if the code is complicated and/or long. Is there a better way?

Yes, use functions.

A function lets you group multiple statements together and give it a suitable name. It keeps your code simple and readable. A good function name reveals the intention of the block of code. Reading a bunch of function calls gives you much better idea about what the code does than reading paragraphs of source code.

Here's what a function named send_email that groups the above lines of code.

def send_email
  to_email = "user@mail.com"
  from_email = "me@email.com"
  subject = "Hello"
  puts "sending email to #{to_email}"
end

Here's the general structure of a function:

def name_of_function(parameter_list)
  # lines of code
end

There’re a few important things to note:

  1. We use the keyword def to tell Ruby that we're creating a function.
  2. Then we give it a name that indicates what this function does, e.g. send_email, along with any information the function needs to perform its job.
  3. Next, we provide the body of the function that does the thing, enclosed between def and end.
  4. Finally, the function definition ends with the keyword end.

In the above example, send_email is the function’s name. It doesn’t need any information to perform its job, so we have omitted the parentheses. The job of the send_email function is to send an email (or at least, print that it’s sending an email).

Once you have defined a function, you have to call that function. A function call instructs Ruby to execute the code enclosed in the function. While calling the function, you also pass any information needed by that function.

send_email

# or 

send_email()

If you're coming from another language, it might surprise you that the braces are not required.

It's important to remember that the code inside the if..end block is not run when you create the function. Ruby will only run the code when you call the function.

You can call a function once, or as many times as you want.

send_email

send_email

send_email

Passing Information: Parameters and Arguments

Now you might be wondering, what if you want to run the block of code with little variations? For example, in the send_email function above, you want to send the email to a different to_email address, instead of user@mail.com?

For this, you can pass any information to a function. A function can receive that information and use it in its body. The information in the function definition is called a parameter, whereas the information you pass when calling that function is called an argument. An argument is a piece of information that’s passed from a function call to a function.

Let's parameterize the email value by rewriting the function as follows:

def send_email(to_email)  
  from_email = "me@email.com"
  subject = "Hello"
  puts "sending email to #{to_email}"
end

To call a function that needs some information, you call the function as above followed by any necessary information you want to pass. Since the send_email function needs the to_email, we will pass that as follows:

send_email("user@test.com")

Note that the parentheses are optional in Ruby, so you could call the function like this:

send_email "user@test.com"
In this case, the variable to_email is a parameter, whereas the value “user@test.com” is an argument.

However, you can have multiple parameters along with blocks (which we’ll learn later) which can make the code confusing to read. Hence, the best practice is to wrap the arguments in parentheses to avoid any confusion.

You can send multiple arguments by separating them with commas:

send_email('user@test.com', 'subject', 'body')

And we can get those parameters in the order they were defined:

def send_email(to, subject, body)
  # ...
end

Parameters can have a default value, so if they're omitted we can still have a value for them.

def send_email(to, subject='test', body='test')
  # ...
end

send_email('test@user.com')

Return Value

A function can return some value to the callee (the code that called the function). The return value can either be the result of some operation that the function performed, or it can be a success or error value, depending on whether the function succeeded or failed.

In most programming languages, you’d use a return statement to return some data back to the callee. Ruby has a return as well, but you don’t use that often, since Ruby returns the value of the expression on the last statement in the function body.

Consider the following function that adds two numbers and returns their sum as result:

def add(a, b)
  sum = a + b
  sum
end

result = add(3, 4)  # 7

You could’ve skipped the intermediary sum variable and written the above function as follows:

def add(a, b)
  a + b
end

Note that when you call a function that returns a value, you need to assign the received value to a variable:

result = add(3, 4)

If you don’t, Ruby will ignore the received value.

Summary

This chapter showed what functions are and how they work in Ruby. we learned how to create functions, how to pass data to them and return and receive data from a function.

Functions also improve your productivity as a programmer. Imagine having to write the email sending code explicitly, again and again, in all the places where you have to send an email.

By writing a function instead, and abstracting all the complicated email-sending functionality behind a send_email function, you hide all that complexity. All you have to do is call that function and it sends the email. What’s more, when you have to change some logic, you do it in one place, where the function is defined.

Functions also let you think on higher levels of abstraction. Once you’ve written a function that sends an email, you don’t have to concern yourself on it again. Now you’re free to think a level higher, about when to send an email, what the email content should be, how frequently should you send it, etc. Once a function is written and tested, you can be sure that it will work and you can move on.


Well, that's all for now, folks. I've been writing non-stop for the past few days, and need to stop somewhere. I really, really hope you found this tutorial helpful and you learned something new. In the next version of this tutorial, we will start our journey into object oriented programming. Stay tuned.

As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.

If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.