Prerequisites

Introduction

Hi! My name is "Truthy". I'm the compass that'll guide you through this course, the hunt for Ruby mastery.

Like all great adventures, we need to start it with a call to action.

action = "victory"
print "Onwards, to " + action + "!"

Output:

Onwards, to victory!

Amazing! You've just written your first Ruby program. Are you ready to go further?

Code!

When we write code, we're writing a list of instructions for our program to execute. It's what makes all of the great apps and websites we use every day!

We'll be executing code throughout this micro-course, so let's have a look at what exactly we'll be asking Ruby to do!

find = "I want to find a "
treasure = "pot of gold!"
print find + treasure

Output:

I want to find a pot of gold!

Boom! print is a so-called method. We use it to display text or other kinds of things.

More on print

There are two ways to display things as output. As we've just seen, print is such a method. Let's have good look at it!

task = "I'm on a treasure hunt!"
print task
print task

Output:

I'm on a treasure hunt!I'm on a treasure hunt!

So print will display everything we ask it to, on the same line. What if we don't want that?

Puts it out there

When print doesn't feel right, we have the option of using the puts method. puts stands for put string and adds a new line after the output.

task = "I'm on a treasure hunt!"
puts task 
puts task

Output:

I'm on a treasure hunt!
I'm on a treasure hunt!

Voilà! We've just made our announcement more legible.

Variables

Variables are important in Ruby. They're a way of giving information a reference that we can use later. We give a value to a variable with an = sign.

mission = "Find the treasure map"
mission = "Find a shimmery lake"
print mission

Output:

Find a shimmery lake

See that? Just as I can only point you in one direction at a time, variables can only point to a single value at a time.

Psst: Ruby will reassign the variable to whatever value we give it, so we need to be careful with those variable names.

Constants

As we know, we can give, or assign, values to variables at any time. There are some values that we want to make permanent, though: constants.

Constants are variables that declared in capitals. How would you go about making my name a constant?

NAME = "Truthy"

Yes! Now my name is a constant, and if we try to change it, Ruby will kindly warn us that its value is permanent.

Strings and numbers

We've seen that variables can point to text, which we call strings. As it turns out, though, variables can store so much more. Numbers, for example.

my_string = "8"
my_number = 8 

Excellent work! The main difference is that strings are declared with quotation marks, whereas numbers can be written just as they are.

Psst: in Ruby, everything is an object: strings, numbers, variables ... we'll be learning a few more objects as we go!

More numbers!

There are two basic kinds of numbers: integers, which are whole numbers, and floats, which are numbers with a decimal place.

my_integer = 3
my_float = 3.0

Oh, what a cute little float!

You know, we can do some pretty cool calculations now that we have numbers.

Operators

Code can do math for us! It uses arithmetic operators, which mainly look like +, -, * and /.

How about we try and multiply these two variables?

first = 8
second = 2
result = first * second
print result
Output:

16

Sweet! Remember when we talked about the print and puts methods displaying different data types? This is a great example of that. They display text and numbers.

A bit more math!

Generally, integers are easier to work with. But Ruby does some funny things when it comes to calculating integers.

If we don't explicitly tell Ruby to calculate with floats, it'll round any result down.

term1 = 8
term2 = 5.0 
print term1 / term2Output:
1.6

Yes! That's the result we're looking for.

Psst: only one of the numbers in this calculation needs to be a float for the result to be correct. Ruby automatically converts the other one.

A basic Adventure

I seem to be doing all of the typing here. How about you open up a text editor and try your hand at writing out what you've learned?

See if you can create two strings and combine them in a single variable!

first_name = "Bob"
last_name = "Jones"
name = first_name + " " + last_name
print name

Output:
Bob Jones

"Bob Jones" is an adventurer. He's going to help us find the tools and clues we need to complete our hunt. Let's find that treasure map, "Bob"!

Introduction

Hi! My name is "Truthy". I'm the compass that'll guide you through this micro-course, the hunt for Ruby mastery.

Like all great adventures, we need to start it with a call to action.

action = "victory"
print "Onwards, to " + action + "!"

//Output Below

Onwards, to victory!

Amazing! You've just written your first Ruby program. Are you ready to go further?

Code!

When we write code, we're writing a list of instructions for our program to execute. It's what makes all of the great apps and websites we use every day!

We'll be executing code throughout this micro-course, so let's have a look at what exactly we'll be asking Ruby to do!

find = "I want to find a "
treasure = "pot of gold!" 
print find + treasure

//Output Below

I want to find a pot of gold!

Boom! print is a so-called method. We use it to display text or other kinds of things.

More on print

There are two ways to display things as output. As we've just seen, print is such a method. Let's have good look at it!

task = "I'm on a treasure hunt!"
print task
print task

//Output Below

I'm on a treasure hunt!I'm on a treasure hunt!

So print will display everything we ask it to, on the same line. What if we don't want that?

Puts it out there

When print doesn't feel right, we have the option of using the puts method. puts stands for put string and adds a new line after the output.

task = "I'm on a treasure hunt!"
puts task 
puts task

//Output Below

I'm on a treasure hunt!
I'm on a treasure hunt!

Voilà! We've just made our announcement more legible.

Variables

Variables are important in Ruby. They're a way of giving information a reference that we can use later. We give a value to a variable with an = sign.

mission = "Find the treasure map"
mission = "Find a shimmery lake"
print mission

//Output Below

Find a shimmery lake

See that? Just as I can only point you in one direction at a time, variables can only point to a single value at a time.

Psst: Ruby will reassign the variable to whatever value we give it, so we need to be careful with those variable names.

Constants

As we know, we can give, or assign, values to variables at any time. There are some values that we want to make permanent, though: constants.

Constants are variables that declared in capitals. How would you go about making my name a constant?

NAME = "Truthy"

Yes! Now my name is a constant, and if we try to change it, Ruby will kindly warn us that its value is permanent.

Strings and numbers

We've seen that variables can point to text, which we call strings. As it turns out, though, variables can store so much more. Numbers, for example.

my_string = "8"
my_number = 8 

Excellent work! The main difference is that strings are declared with quotation marks, whereas numbers can be written just as they are.

Psst: in Ruby, everything is an object: strings, numbers, variables ... we'll be learning a few more objects as we go!

More numbers!

There are two basic kinds of numbers: integers, which are whole numbers, and floats, which are numbers with a decimal place.

my_integer = 3
my_float = 3.0

Oh, what a cute little float!

You know, we can do some pretty cool calculations now that we have numbers.

Operators

Code can do math for us! It uses arithmetic operators, which mainly look like +, -, * and /.

How about we try and multiply these two variables?

first = 8
second = 2
result = first * second
print result

//Output Below

16

Sweet! Remember when we talked about the print and puts methods displaying different data types? This is a great example of that. They display text and numbers.

A bit more math!

Generally, integers are easier to work with. But Ruby does some funny things when it comes to calculating integers.

If we don't explicitly tell Ruby to calculate with floats, it'll round any result down.

term1 = 8
term2 = 5.0 
print term1 / term2

//Output Below

1.6

Yes! That's the result we're looking for.

Psst: only one of the numbers in this calculation needs to be a float for the result to be correct. Ruby automatically converts the other one.

A basic Adventure

I seem to be doing all of the typing here. How about you open up a text editor and try your hand at writing out what you've learned?

See if you can create two strings and combine them in a single variable!

first_name = "Bob"
last_name = "Jones"
name = first_name + " " + last_name
print name

//Output Below

Bob Jones

"Bob Jones" is an adventurer. He's going to help us find the tools and clues we need to complete our hunt. Let's find that treasure map, "Bob"!


Types

It's high time we introduce another data type, namely, booleans. They're rather simple because they come in just two flavors: true and false.

How do we differentiate them from strings and integers?

my_boolean = true 

Yup! We create booleans without quotation marks. That's how Ruby knows to evaluate them as booleans.

Psst: did you know that booleans are objects as well?

Equal?

We'll dig deeper into this a little later but, behind the scenes of Ruby, everything is evaluated as true or false. That's great for comparisons.

Let's see how a comparison works.

number_1 = 5
number_2 = 5 
print number_1 == number_2

//Output Below

true

See that? We call == a comparison operator. It asks whether the value to its right is equal to the value on its left.

Psst: while we write from left to right, Ruby reads what's on the right first.

Greater or less?

I'm sure you're wondering what other comparisons we can make. Well, let's see!

Which operator would ensure false is printed here? 

number_1 = 5 
number_2 = 8
print number_1 > number_2

//Output Below

false

Yup, we can ask Ruby whether number_1 is greater or less thannumber_2. Its response will be a boolean.

Not just numbers

The comparison operators we've learned about so far don't just work with numbers, they also work with strings.

Which operator will make the comparison below evaluate to true?

print "treasure" > "hunt"

//Output Below

true

Nice! Ruby takes the first letter from every string and compares them. The lowercase alphabet is stored from smallest to largest, so "h" is less than "t".

But also equal

Let's go back to numbers and add another nifty operator to our repertoire! Can you spot an operator that'll make the value returned below true?

print 5 >= 5

//Output Below

true

Yessiree! After all, >= means greater than or equal to, just as <= means less than or equal to.

Psst: since the comparison of 5 and 5 satisfies one of the conditions, the result is true.

! Not !

Ruby has the power of not, which uses an ! sign and is kind of like a double negative.

Knowing that, let's read the line below and try to anticipate the output.

print !true

//Output Below

false

I did hear you say false, right? The line literally reads "print not true", which is false in boolean terms.

Not equal

There's an extra bit to the ! operator.

Let's make sure this code snippet displays true!

print 2 != 3

//Output Below

true

Excellent! != translates to not equal to, and since 2 doesn't equal 3, that evaluates as true.

And

Let's carry on with these so-called logical operators! They don't just exist as symbols but can be words, too.

The and operator is one of them but, as per convention, Rubyists use &&.

print 2 < 3 && 3 == 3 and true

//Output Below

true

Huh, look at that! The logical and will return true if both conditions on either side of it are true. It's compare-ception!

O rly?

Alright, ready for another one? Let's check out the logical or, otherwise written as ||.

These odd-looking symbols are called pipes. Much like with &&, Ruby will look for comparisons on both sides of the operator.

print 2 < 3 || 3 == 3 

//Output Below

true

See that? Only one of the values we compare has to be true for the || to evaluate to true.

Psst: in the next chapters, we'll get a better understanding of how useful these logical operators are.

Halt! Reserved words!

Ruby has reserved words that we can't use for naming things because they already serve a purpose as keywords.

Can you anticipate which word is not off-limits?

string = "I can't be stored in a keyword"

Fantastic! While we can't use keywords like true, false, not, and and or, we can name variables integer or string. In fact, we already did that, didn't we?

Calling all operators

We've learned about booleans, comparison operators, and logical operators. That's quite a bit, right?

We've seen lots of examples of these put together, but let's go over it one last time! Gimme some truth!

letters = "scavenge" < "jewels"
print letters || 3 == 9/3

//Output Below

true

I'm starting to think that you don't need my guidance for this. Look at how beautifully we've combined all of those operators!

Adventure of Truth

"Bob" has met a traveller that has made an absurd claim. How can we tell her that her claim is false?

Create two comparisons and assign them to two variables that you compare to each other. Be sure to display false in the output.

lies = 'bacon' == 'apples'
honesty = "bacon" > "apples"
print lies && honesty

//Output Below

false

The traveller took this proof very well and "Bob" can carry on with his adventures!


While

Now that we've understood booleans, we can put them to real use with loops.

First up, the while loop, which repeats a code snippet over and over until a condition that it's given isn't met anymore.

count = 0
while count < 4
 print "Ruby! "
 count = count + 1
end

//Output Below

Ruby! Ruby! Ruby! Ruby!

Indeed! The while loop will display "Ruby " and add 1 to count variable while it's less than 4.

While 2.0

Did you notice I said that loops repeat themselves until a condition isn't met anymore?

This condition goes after the while keyword and can use any comparison or logical operator.

count = 4
while count != 0
 print "Loopy! "
 count = count - 1
end

//Output Below

Loopy! Loopy! Loopy! Loopy!

Sweet! We've told the loop to run while count isn't equal to 0 and are subtracting 1 from count in every iteration, until the condition is false.

Until

Let's look at the until loop, shall we? Unlike the while loop, it runs until a condition is met.

count = 5
until count == 0
 print 'Dizzy? '
 count = count - 1
end

Nice! I didn't even have to explain that this code snippet will run until count is exactly equal to 0, did I?

Until 2.0

We can use boolean operators with loops, too. Look!

n_1 = 1
n_2 = 2
until n_1 == 4 && n_2 == 5
 print 'Whirly! '
 n_1 = n_1 + 1
 n_2 = n_2 + 1
end

//Output Below

Whirly! Whirly! Whirly!

That's right! What's different here is that we're adding 1 to both of the counters, until both conditions are true.

Ranges

Ranges are handy things in Ruby. We create so-called inclusive ranges with .. and exclusive ranges with .... But what does that mean?

Let's take a look at what the first one does.

print *1 .. 5

//Output Below

12345

Nice! The inclusive range will display 12345 because it includes the last number

Ranges 2.0

When we want to have a range that does not include the last number, we use ....

Can you guess what other sign is needed to make this range work?

print * 1 ... 5

//Output Below

1234

Great work! We create an exclusive range by using .... The range will display 1234 and exclude the 5.

Psst: the * sign is also known as the splat operator. We'll come back to it in a bit, okay?

For for for!

So now that we've mastered ranges, we can go into for loops, which are great when we know exactly how many times we want the loop to run.

for number in 1 .. 5
 print "Again! "
end

//Output Below

Again! Again! Again! Again! Again!

Huzzah! The for loop uses a temporary control variable and says: for every number in the range, repeat what's between the loop and the end keyword.

Psst: we can use any name we like for the control variable. Many folks use i, for example.

Infinity and beyond!

So we've looked closely at the while and until loops but, at times, they can be risky.

If we don't cut them off at some point, they will go into an infinite loop, which causes programs to ... crash.

count = 0
while count != 3
 print 'Infinity'
end

//Output Below

InfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinityInfinity

Eeeek, we forgot to increment or decrement count! That way, the loops keeps running because count will never be anything other than 0.

Code blocks

We've been working with code blocks, but what exactly are they? Well, they're snippets of code that exist in between keywords.

count = 0
while count < 2
 print "I'm a block! "
 count = count + 1
end

//Output Below

I'm a block! I'm a block!

Fantastic! while, until and for at the beginning and end at the end are keywords that make code blocks.

Assignment operators

We've been doing a lot of increasing or decreasing variables by 1 and reassigning them to the original variable. Might there be a shortcut?

count = 1
print count += 1

//Output Below

2

Isn't that handy? These assignment operators perform the operations they're supposed to but automatically reassign the results to the variables before them.

Psst: we can also use -=, *=, /= to shorten our code.

Reservations

Do you remember when we talked about reserved words? Well, we can add these recent keywords to the list because they're reserved as well.

for loop in 1 ... 5
 print "All work and no play makes Johnny a dull boy. "
end

//Output Below

All work and no play makes Johnny a dull boy. All work and no play makes Johnny a dull boy. All work and no play makes Johnny a dull boy. All work and no play makes Johnny a dull boy. 

Of course, while, until, for and end are off-limits. You remember that and, or, not, true and false are also on the list, right?

All together now!

We've just wrapped our incredible minds around while, until and for loops as well as ranges, code blocks, and assignment operators.

Not bad. Not bad at all. Now, what's the output for this loop going to be?

for i in 1 .. 5
 j = 1
 while j <= i
  print j
  j += 1
 end
end

//Output Below

1 12 123 1234 12345

See that? We can put loops within other loops to create nested loops.

Looped Adventure!

Let's help "Bob Jones" keep his momentum going and put him in the loop about his treasure-hunting progress.

Create a loop that'll display some encouraging words until "Bob" has found four treasures. Also, be sure to mention how many he's got left.

treasures = 0
until treasures == 4
 treasures += 1
 print "Sweet, #{4 - treasures} to go! "
end

//Output Below

Sweet, 3 to go! Sweet, 2 to go! Sweet, 1 to go! Sweet, 0 to go! 

"Bob" and us, we're most definitely on the hunt and making great progress!

If

So now we know what booleans are and how loops work. What if we want to execute a code block based on some condition, though?

filling = "bacon"
if filling == "bacon" 
  print "Bacon sandwich!"
end

//Output Below

 sandwich!

Of course: the if keyword! Since the condition is true, Ruby will execute the code block between if and end.

Else

What if we don't want a bacon but a cheese sandwich?

Well, we can jump to another code block if the condition we provide after the if keyword is false.

filling = "cheese"
if filling == "bacon"
 print "Bacon sandwich!"
else 
 print "Cheese sandwich!"
end

//Output Below

Cheese sandwich!

Correct! These keywords are also called conditional statements. They're saying: if this is true run this code block; else, if it isn't true, run this other code block.

Elsif ...

Wait, but what if we don't want either one of those sandwiches?

filling = "egg salad"
if filling == "bacon"
  print "Bacon sandwich!"
elsif  filling == "cheese"
  print "Cheese sandwich!"
else
  print "Egg salad! Yum!"
end

//Output Below

Egg salad! Yum!

Yes! If the condition after the if keyword is false, we can use elsif statements with additional conditions. If none of them are met, the else block will run.

Conditional statements

Conditional statements like if and elsif require a condition to be true. The else statement, though, is executed if none of these conditions are met.

hunger = false
if hunger
 print "Give me sandwiches!"
else 
 print "I don't want sandwiches."
end

//Output Below

I don't want sandwiches.

Great! The else part will execute without a condition if the conditional statements before it resolve to false.

More truth

Okay, this can be a little confusing. What do you think the code below will display?

hunger = true
if !hunger
 print "I don't want sandwiches"
else
 print "Give me sandwiches!"
end

//Output Below

Give me sandwiches!

Huh, a bit of a mind warp, no? Since the if statement uses the inverse value of hunger, the condition isn't met and the else block is executed.

Many returns

There's a catch with multiple met conditions, though. What might it be?

booly = true
if booly
 print "Hurrah!"
elsif booly && 1 + 1 == 2
 print "Hurrah again!"
else
 print "No hurrahs."
end

//Output Below

Hurrah!

See that? Ruby exits the structure as soon as a condition is met and, therefore, never makes it to the elsif statement we just added.

Implicit returns

So we're getting into the idea of returns.

Let's make the code below return a string, without using print, puts or return.

my_string = "Wubba dub dub"
if 8 < 10
  my_string
end

//Output Below

Wubba dub dub

Oo fancy pants. Ruby is a clever language, because it handles implicit returns. It will assume you want output if your code is standing alone like that.

It'll give you the value of a variable, the result of a calculation, or even a string!

Ternary Operator

There is one more way of declaring an if/else statement, and it is called a ternary operator.

It is a one line statement that follows the same logic of if and else, but uses different symbols instead. Can you figure out the symbol for if?

thirsty = true 
thirsty ? "Water!" : "No, thanks."

//Output Below

Water!

The first statement in the ternary operator is a condition and is followed by the operator ?, which stands in for the if  keyword. The : represents else.

If the condition is true, the code before the : will be executed. If it is false, the code after the : will run. Cool huh!?

Unless

Let's have a look at another conditional statement. It'll be the last statement for now. Can you guess which one it is?

sleepy = false
unless sleepy
 "I've had too much coffee!"
else
 "Naps are the best!"
end

//Output Below

I've had too much coffee!

The unless keyword kind of turns things around. It executes a code block if its condition is false, making it the exact opposite of if and elsif.

Everything under control

The if, elsif, else, unless and return keywords are some the structures that constitute control flow.

x = -10
if x > -5
 unless x > 5
  print "In between -5 and 5"
 else
  print "Greater than 5"
 end
else
 print "Less than -5"
end

//Output Below

Less than -5

Sweet! We can use them to control the execution of a program.

Psst: you're perfectly aware that those words are also reserved, right?

Returning our Adventure

We should really double-check the weather before we accompany "Bob" on his adventures.

Create a weather variable and build a control flow with three conditional statements. Display different memos for "Bob".

weather = "sunny"
if weather == "cloudy"
 print "Nothing to worry about!"
elsif weather == "rainy"
 print "Bring an umbrella!"
else
 print "Sunglasses out!"
end

//Output Below

Sunglasses out!

Well, the sun is shining on the treasure hunt! We're game, "Bob"!

List it out

Arrays are special variables in that they hold multiple values of any data type in an ordered and indexed list.

Let's take a stab at creating an array!

array = [] 

Excellent work! Arrays are declared with [] and the values they hold go between those brackets.

Another object! They're everywhere!

Empty ... or full?

We don't have to start with an empty array, though. We can create an array with values from the get go. Would you mind?

treasure = ["jewels", "gold"]

Sweet! The values in an array are separated by commas.

Accessing values

So, what if we want to get a specific value out of this shiny array?

You probably remember that arrays are ordered by an index, right? How would we go about printing "jewels"?

treasure = ["jewels", "gold"]
print treasure[0]

//Output Below

"jewels"

Sweet! We can ask Ruby to look in the treasure array at the index of 0 and return the value that lives there.

Psst: like in most programming languages, indices in Ruby start at 0.

Using values

Great! So now that we know how to access the values, let's do something with them!

nums = [2, 3]
print nums[0] + nums[1] + nums[-1]

//Output Below

8

Great! We've referenced the values by their index and added them together.

Psst: did you notice the -1 ? That's a reversed way of accessing values in an array. -1 is the last index in an array, -2 is the second to last and so on.

Push it!

Do you remember when we talked about methods for strings?

There are lots of methods for arrays, too. Let's find a method to add another value to the treasure array!

treasure = ["jewels", "gold"]
treasure.push("silver")
print treasure

//Output Below

["jewels", "gold", "silver"]

Woot woot! The push method adds whatever is in between the parentheses to the end of the array.

Length

Might there be a way to tell how long an array is? Yes! In fact, we've seen this method before!

tools = ["shovel", "map", "compass", "rucksack"]
print tools.length 

//Output Below

4

Exactly! Again, there are multiple ways to find this out: Besides length, we can also use count and size to tell us how many values there are in an array.

Psst: count is an array method, which means that it won't work on strings.

Sorting

You know what else we can do with a method? We can sort an array!

numbers = [5, 2, 4, 3, 1]
print numbers.sort 

//Output Below

[1, 2, 3, 4, 5]

Voilà! The number jumble is sorted from smallest to greatest with the sort method.

Iterating

There's a lovely method that lets us go through each value in an array.

treasure = ["jewels", "gold"]
treasure.each do |piece|
 puts piece.capitalize
end

//Output Below

Jewels
Gold

Wild! We've asked Ruby to iterate over the treasure array and display each value in capitalized form.

There's quite a bit happening here, so let's have a look at this code block!

Code blocks

There are two ways of declaring code blocks: in between braces or in between the do and end keywords.

tools = ["Shovel", "Map"]
tools.each do |tool|
 puts tool.upcase
end

//Output Below

SHOVEL
MAP

Nice! Did you notice the pipes? They create tool, the variable in between them, as a placeholder variable that only exists within the code block.

Every time we iterate over the array, tool takes on the value at that index so that we can use it.

Many times

Okay, here's the method for the road.

Now that we've seen code blocks in some detail, how about we display this statement three times?

3.times { print "Go! " }

//Output Below

Go! Go! Go!

See that? The times method repeats the code block in the braces however many times we tell it to.

All of the arrays

So we've covered arrays, those ordered and indexed lists of values. Also, we've seen how to access and use their values and a number of related methods.

say = ["I","love", "arrays"]
say.each { |word| print word + " "}

//Output Below

I love arrays 

Right! We've told Ruby to display each value in the say array with a space after it.

Array-y Adventure

Together with "Bob", we need to decide which path to follow, so let's consult our map!

Create a paths array with a few values. Then display how many possibilities there are, go through them one-by-one and pick the last one.

paths = ["North", "South", "East"]
puts "There are #{paths.count}" class="subst paths that we can take"
paths.each { |path| puts "We can go " + path }
puts "Let's go " + paths[-1]

//Output Below

There are 3 paths we can take
We can go North
We can go SouthWe can go East
Let's go East

If "Bob Jones" has chosen to go east, that's where we'll go as well!

Another type of list

So we know about ordered lists, namely, arrays. This type of list, however, is a different story.

Hashes are unordered lists of values, in which each value has a key that points to it. Again, how might we create such a hash?

hash = {} 

Spot on! {} creates an empty hash.

Psst: we've also used {} to declare code blocks but, this time, we're assigning the braces to a variable instead of placing them after a method.

Key-value pairs

So how do we get values into such a hash?

In short, by adding them as part of key-value pairs. Since hashes are unordered, we need something else to make their values accessible: keys.

hash = { "name" => "Dipper" }
print hash["name"]

//Output Below

Dipper

See that? We've just added a so-called key-value pair to hash and asked Ruby to display the value that belongs to its key, "name".

Psst: while we can use =>, which to assign a value to a key, that's not the only way to get there.

Growing the hash

Great! Now, how might we go about adding key-value pairs to a hash?

ruby = { "gem" => true }
ruby["color"] = "red"
print ruby

//Output Below

{"gem"=>true, "color"=>"red"}

Ta-da, we've added a key-value pair: a string key that points to a boolean value.

Psst: did you notice how the output places a => sign between key-values pairs? Well, we can also use this so-called rocket sign to create hashes.

Changing values

What if we decided we wanted to change a value in a hash?

ruby = { "color" => "pink" }
ruby["color"] = "black"
print ruby

//Output Below

{"color"=>"ruby"}

Wow, how easy was that? Just like we add a key-value pair, we can replace a value with something different.

Keys

We can store as many key-value pairs in a hash as we want to. It can have duplicate values but its keys have to be unique.

adventure = { "tool" => "map", 
          "another_tool" => "map" }
print adventure["tool"]

//Output Below

{"tool"=>"map", "another_tool"=>"map"}

That's right! Keys have to be unique, otherwise it'd be impossible to find the right value in the hash.

Data types

As with arrays, keys and values in hashes can be of any data type.

tool = { "name" => "map", 
       "amount" => 1, 
    "available" => true }
puts tool["name"]
puts tool["amount"]

//Output Below

map
1

How great is that? We can use strings and numbers as keys and values. By putting the key in between brackets, we can access the value it points to.

Psst: unless we wrap them in quotation marks, we can't use keywords like for or if as keys, though.

Iteration

So we've got hash basics down to a tee. Let's try our hand at some methods!

We're familiar with the each method when it comes to arrays but it works a little differently for hashes.

origins = { "Columbus" => "Italian", "Cousteau" => "French" }
origins.each do |key, value|
 puts "#{key} was #{value}"
end

//Output Below

"Columbus was Italian"
"Cousteau was French"

Wowzers! We have to use two of those placeholder variables because the key-value pairs in a hash consist of two parts: a key and a value.

Size

Just so as not to lose the habit, what can we use to determine the size of a hash?

tool = { "name" => "compass", 
       "amount" => 1, 
    "available" => true }
print tool.length

//Output Below

3

You knew it, didn't you? Don't forget, we can also use the size and the count methods to get the number of elements in a hash.

Key?

So, what if we have a huge hash and don't know if a key has already been used?

gem = { "name" => "ruby", 
       "color" => "red" }
gem.has_key?("shape")

//Output Below

false

Right! By convention, methods that end in a ? will return a boolean. Since "shape" isn't a key in the gem hash, has_key? and key? return false.

Psst: we can also use thekey? method.

Value?

You know what else we can look for in a hash? Values!

gem = { "name" => "ruby",
       "color" => "red" }
print gem.has_value?("red")

//Output Below

true

Sweet! Seeing as we can ask Ruby about existing keys and values, it's easy to begin navigating our hashes!

Hash hashes

We're blazing this trail! We know about keys, values, key-value pairs, how to iterate over hashes and a bunch of really useful methods.

tools = { "shovel" => "digs",
             "map" => "directs" }
tools["flashlight"] = "illuminates"
tools.each { |tool, action| puts "The #{tool} #{action}" }

//Output Below

The shovel digs
The map directs
The flashlight illuminates

Yep, on a serious roll! It's time to make your own hash, isn't it?

Hashing out an Adventure

How many levels have we completed together with "Bob Jones"?

Create a hash with three key-value pairs with different data types (for example "name", "levels" and "retired") and display the values using interpolation.

explorer = {"name" => "Bob Jones", 
           "levels" => 7, 
          "retired" => false }
print "#{explorer['name']} has leveled up #{explorer['levels']} times!"

//Output Below

Bob Jones has leveled up 7 times!

Great work! Of course "Bob" won't retire before we finish this micro-course.

What it is?

So, what exactly are methods? In so many words, they're sections of reusable code.

gems = ["ruby", "opal", "diamond"]
length = 0
gems.each do |gem|
 length += 1
end
print length

//Output Below

3

See that? These lines do the same thing as the length method. Would you want to write them every time you wanted to know the length of an array?

Defining methods

So far, we've only been using built-in methods that Ruby provided us with. We can also define our own methods, though!

def puts_numbers
 1..5.each { |num| print num }
end

Well done! We define a method with the def keyword and a name. Below that definition, we put a code block that we finish with end.

Using parameters

There's an extra bit to methods, and that's parameters. When we add them to a method, that method will require that we pass it a value when we call it.

def greet(explorer)
 puts "Hello #{explorer}!"
end

Great! Parameters are variables that we use within a method to access the values, or arguments, that we pass to the method.

Psst: it's a great idea to use descriptive names for methods and variables.

Calling methods

Now that we have a greet method that takes a parameter and greets an explorer, how can we get it to work?

def greet(explorer)
 puts "Hello #{explorer}!"
end
greet("Dipper") 

//Output Below

Hello Dipper!

Excellent! We use the name of the method and an argument within parentheses to get there. That's what we call, well, calling a method.

Psst: if a method has a parameter, we kind of have to pass it a value.

Many parameters

Methods can take more multiple parameters, of course. Let's modify the greet method to allow us to change the actual greeting.

def greet(greeting, explorer)
 puts "#{greeting} #{explorer}!"
end
greet("Hi there", "Bob")

//Output Below

Hi there Bob!

See that? We separate parameters (and arguments) with a comma. While we can use as many of them as we want, it's a good idea to keep them to a minimum.

SPLAT!

We talked about the * sign, which is also known as the splat operator, very briefly a few chapters ago.

In short, the * sign creates flexibility. If, for example, we don't know how many parameters we need to give a method, we can use the splat operator.

def greet(greeting, * explorers)
 explorers.each do |explorer| 
  puts "#{greeting} #{explorer}!"
 end
end
greet("Yo", "Bob", "Maria")

//Output Below

Yo, Bob!
Yo, Maria! 

Sweet! The * sign lets us pass in as many values for a single parameter as we want.

Return again

We've talked about return, but I think we should do it again!

The return keyword, well, returns a value. This means that we can assign the result of a method to a variable. Wild!

def triple(number)
 return number * 3
end

my_number = triple(3)
print my_number

//Output Below

9

Woohoo! The triple method takes an integer, multiplies it by 3 and returns the result. We call it, store its return value in my_number and display it.

So what?

You might be thinking that it's rather pointless to return a value if we can just display it directly from the method.

Well, if we store this value in a variable, we can do all sorts of things with it.

def triple(number)
 return number * 3
end
def triple_pie_count(number)
 puts "#{number} pies! Yay!"
end

pies = triple(3)
triple_pie_count(pies)

//Output Below

9 pies! Yay!

Pies? Does anybody need more pies?

Nesting

Let's cover another bit: nesting. Actually, we've done it without even realizing it, by using methods inside other methods.

def triple(number)
 return number * 3
end
def triple_pie_count(number)
 pies = triple(number)
 puts "#{pies} pies! Yay!"
end

triple_pie_count(1)

//Output Below

3 pies! Yay!

Look at that! We used the triple method *within** the epic triple_pie_count method.

Boolean methods

When we want a method to return a boolean value, there's a convention for that.

def is_gem?(stone)
 if stone == "ruby"
  true
 else
  false
 end
end

That's right! When we define a method to end with a question mark, it's clear that the method will return true or false in response to the kind-of question.

Methods acting

Whoa! We've covered method definitions, parameters, what boolean methods look like, what nesting is and why the return keyword is a thing.

def cheer
 print 'Go explorer!'
end

cheer
cheer
cheer

//Output Below

Go explorer! Go explorer! Go explorer!

See? Methods are useful, reusable sections of code.

Methodical Adventure

"Bob Jones" has found a treasure! He needs a way to add it all to his satchel but he doesn't know how much of it there really is.

Create an add method that adds any number of items to a satchel array and displays what's in the array in the end.

def add(*items)
 satchel = []
 items.each do |item|
  satchel.push(item)
 end
 print satchel
end

add('tin can', 'wrapper', 'bottle')

//Output Below


["tin can", "wrapper", "bottle"]

Uh, that's some weird treasure! It looks more like "Bob Jones" is picking up trash, doesn't it?

Classes

Classes are kind of like factories that create objects with states and behaviors. As almost everything in Ruby, they're objects themselves.

class Explorer
  attr_reader :name
  def initialize(name)
   @name = name
  end
end

explorer = Explorer.new("Bob")
print explorer.name

//Output Below

Bob

Awesome! We've just created an instance of the Explorer class and assigned it to explorer, a variable. Let's dig a little deeper!

Syntax

A class is very simply declared with the class keyword and a name that starts with a capital letter.

class Explorer 
end

That's it! Naming conventions are big in Ruby, not only for readability reasons but because Ruby does specific things based on these names.

Attribute reading

Attributes are variables that we can access from outside of a class. We've seen a weird line in the Explorer class that was related to these attributes.

attr_reader :name

Sweet! This line gives us reading access to the name attribute. Without it, we wouldn't be able to say explorer.name and get "Bob" returned to us.

Initialization

The initialize method is a built-in method for any class that gets an instance of the class ready when we call new on it.

class Explorer
 attr_reader :name
 def initialize(name)
  @name = name
 end
end

explorer = Explorer.new("Bob")
print explorer.name

//Output Below

Bob

Excellent! Now explorer is an instance of the Explorer class.

Psst: did you notice that initialize can have parameters?

State

So, we created an Explorer class and gave it a state, the name attribute. These attributes are stored in so-called instance variables.

class Explorer
 attr_reader :name
 def initialize(name)
  @name = name
 end
end

explorer = Explorer.new("Bob")
print explorer.name

//Output Below

Bob

That's it! We create instance variables with a @ sign and access them with a . sign through an instance of the class.

Psst: states don't do anything, they just store information.

Behavior

Classes aren't much use to us if they don't have some kind of behavior, which is what we've come to know as methods.

class Explorer
 attr_reader :name
 def initialize(name)
  @name = name
 end
 def eat
  print "Munch!"
 end
end

explorer = Explorer.new("Bob")
explorer.eat 

//Output Below

Munch!

Yessir! The instance of the Explorer class that we stored in explorer can eat. Are you getting the drift here?

Instances

The methods that we define in a class are only available to the class and to instances of the class. With that in mind, what do you think this code snippet will display?

class Explorer
 attr_reader :name
 def initialize(name)
  @name = name
 end
 def eat
  print "Munch!"
 end
end

explorer = "Maria"
print explorer.eat

//Output Below

undefined method 'eat' for "Maria":String (NoMethodError)

Whoa! Well, explorer isn't really an instance of the Explorer but of the String class, so the it doesn't know anything about the eat method.

Psst: as explorer is a string, we can use methods like length on it.

Multiple states

Explorers don't just have names, do they? Luckily, we can add as many states to classes as we want.

class Explorer
 attr_reader :name, :age, :origin
 def initialize(name, age, origin)
  @name = name
  @age = age
  @origin = origin
 end
end

explorer = Explorer.new("Bob", 30, "Atlantis")
explorer.name 

//Output Below

Bob

See that? The explorer variable is an instance of Explorer, which means it can hold loads of goodies for us to use.

Multiple behaviors

Guess what: classes can have many behaviors, too.

class Explorer
  def eat
    print "Munch!"
  end
  def sleep
    print "Zzzz.."
  end
end

explorer = Explorer.new
explorer.eat

//Output Below

Munch!

That's right! Just as the String class has methods like upcase or capitalize, we can have many methods to achieve many more or less useful things.

Psst: have you noticed something odd? This Explorer class doesn't have an initialize method, so we can't pass "Bob" when we call new.

Interaction

So we've understood the separation between state and behavior, but did you know they can work together?

class Explorer
 attr_reader :steps
 def initialize
  @steps = 0
 end
 def walk
  @steps += 1
 end
end

explorer = Explorer.new
explorer.walk
print explorer.steps

//Output Below

1

See that? Methods often change instance variables and, therefore, the state of an instance.

Rounding up classes

Now we know what a class is, what states and behaviors are for and how to create classes and instances. That's really great!

class Sandwich
 attr_reader :filling
 def initialize
  @filling = []
 end
 def add(filling)
  @filling.push(filling)
 end
end

sandwich = Sandwich.new
sandwich.add("cheese")
sandwich.add("bacon")
sandwich.filling

//Output Below

["cheese", "bacon"]

Yum, what a sandwich!

Classy Adventure

Boy, is it dark in this cave! "Bob" is supposed guide us but he can't even see the map. What we need more than anything else is a flashlight.

Create a Flashlight class with an initialize method, a state and behavior. Create and instance, use a switch_on method and display the state.

class Flashlight
 attr_reader :on
 def initialize
  @on = false
 end
 def switch_on
  @on = true
 end
 def switch_off
  @on = false
 end
end
   
light = Flashlight.new
light.switch_on 
print light.on

//Output Below

true

Hooray, we can continue exploring!

What's in a module?

Modules allow us to group methods but also classes and constants in a meaningful way. Unlike classes, modules can't be instantiated.

module Noise
 def make_noise(sound)
  print "#{sound}!"
 end
end

Yes! We use the module keyword and a name to define a module.

This module kind of looks like a class, doesn't it?

Calling modules

We call methods in a module like we call class methods, except that we call them directly on the module.

module Noise
 def make_noise(sound)
  print "#{sound}!"
 end
end

Noise.make_noise("Boo")

//Output Below

undefined method \\\\\\`make_noise' for Noise:Module (NoMethodError)

Whoa! Why does this throw an error? We call the make_noise method directly on Noise because we can't create instances of modules, right?

Including modules

The question is: how can we make Ruby find that Noise module? Well, we have to include it where we need it.

module Noise
 def make_noise(sound)
  print "#{sound}!"
 end
end
class Duck
 include Noise
end

duckling = Duck.new
duckling.make_noise('Quack')

//Output Below

Quack!

Excellent! Since we included the Noise module, we didn't have to create the make_noise method in the Duck class in order to use it.

Usefulness

Are you wondering why we'd create a whole module to include a method that we could just put into a class?

Well, remember that we like to write reusable code. And ducks aren't the only objects that make noise, are they?

module Noise
 def make_noise(sound)
  print "#{sound}!"
 end
end
class Cow
 include Noise
end

calf = Cow.new
calf.make_noise("Moo")

//Output Below

Moo!

Isn't that something? It's like the make_noise method was in the class all along.

More!

Nothing stops us from creating class methods in classes that include modules, though.

module Noise
 def make_noise(sound)
  print "#{sound}!"
 end
end
class Dog
 include Noise
 def distract
  print "SQUIRREL! "
 end
end

puppy = Dog.new
puppy.distract
puppy.make_noise("Woof")

//Output Below

SQUIRREL! Woof!

Modules are used to share behaviors between classes. They save us the hassle of writing the same methods over and over.

Constants

There's more! Modules can have constants. This is another handy thing for separating code.

module Quadruped
 LEGS = 4 
end

While we can't have states, we can have constants. We've just declared a LEGS constant in a module we called Quadruped.

Psst: when we include this module in a class, the constant will be available for us to use in that class.

Inclusion

No matter what we've chosen to put in that additional module, we include it in our classes in the same way.

class Dog
 include Quadruped 
 def stand
  "Has #{Quadruped::LEGS} legs!"
 end   
end

puppy = Dog.new
puppy.stand

//Output Below

Has 4 legs!

See that? We access the module's constant using the name of the module and a :: sign.

Mixins

The modules we're including in classes are also known as mixins. Let's include multiple mixins in this Sheep class!

def Sheep
 include Noise
 include Quadruped
 def number_of_legs
  Quadruped::LEGS
 end
end

sheep = Sheep.new
sheep.make_noise('Baaah')
sheep.number_of_legs

//Output Below

Baaah!
4

Ta-da! You're getting this!

Namespaces

You know what else we can do? We can use modules as containers, or namespaces, for classes, constants and other modules.

module Adventure
 TOOLS = ["map", "compass"]
 class Person
  attr_reader :name
  def initialize(name)
   @name = name
  end
  def use(tool)
   "#{@name} used the #{tool}!"
  end
 end
end

Sweet! Modules can group together methods, constants and classes that are related to each other and give them a name we can relate to.

Using namespaces

So the Adventure module contains a Person class and an TOOLS constant.

There are more things an adventure can have, but we naturally understand that all of these things are part of an adventure. How do we call them, though?

p = Adventure::Person.new("Bob")
p.use(Adventure::TOOLS[0])

//Output Below

Bob used the map!

See that? If that's too longwinded, we can also use include to, you know, include the whole module.

Psst: remember that we access classes and constants in a module with the :: sign, or notation.

Much modules

So we've mastered modules, right? We know that they can be mixins or namespaces and that they can include methods, constants and classes.

class Explorer
 include Noise
 attr_reader :name
 def initialize(name)
  @name = name
 end
end

bob = Explorer.new("Bob")
bob.make_noise("Wubba lubba dub dub")

//Output Below

Wubba lubba dub dub

Now pretty much everybody can make noise. Brilliant, brilliant work!

Modular Adventure

We seem to be reaching the spot on the map marked X. There's but a last hurdle to take.

Create a Discovery module with a method. Include it in the Explorer class and call the method on bob, an instance of the class.

module Discovery
 def jig
  puts "#{@name} does a jig!"
  puts "We mastered Ruby!"
 end
end
class Explorer
 include Discovery
 attr_reader :name
 
 def initialize(name)
  @name = name
 end
end

bob = Explorer.new("Bob")
bob.jig

//Output Below

Bob does a victory jig!We mastered Ruby!

Introduction

Hello there and welcome, we'll be starting a new programming language called Ruby. Ruby is an easy-to-learn and powerful programming language used for building web apps and scripting.

And before we start, we need to set up a few things.

What we'll need

First of all we'll need to install Ruby.

We have several tools on each major platform to install Ruby:

  • On Linux/UNIX, you can use the package management system of your distribution or third-party tools (rbenv and RVM).
  • On macOS machines, you can use third-party tools (rbenv and RVM).
  • On Windows machines, you can use RubyInstaller.

Ruby comes with a program that will show the results of any Ruby statements you feed it.This program is called IRB(which stands for Interactive Ruby).

  • If you’re using macOS open up Terminal and type irb, then hit enter.
  • If you’re using Linux, open up a shell and type irb and hit enter.
  • If you’re using Windows, open Interactive Ruby from the Ruby section of your Start Menu.

You will then see something like this

irb(main):001:0>

Ok, so it’s open. Now what?

Type this: "Hello World"

And Ruby obeys you by replying this

irb(main):001:0> "Hello World"
=> "Hello World"

Now you wouldn't enjoy typing your whole futurist program in IRB right?

We can prevent doing the above by choosing any text editor of our choice like gedit or visual studio code and enter or program into it then we save it as myRubyApp.rb.

Now to run this program all we have to do is open the terminal or the shell and type  

Ruby myRubyApp.rb

And hit enter, and you will see your program run