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?
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.
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?
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 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.
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.
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!
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.
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.
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.
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"
!
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?
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.
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.
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"
.
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
.
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.
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
.
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!
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.
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?
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!
"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!
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
.
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
.
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?
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 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
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?
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.
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
.
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.
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.
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?
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.
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!
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
.
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.
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 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
.
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.
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.
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!
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!?
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
.
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?
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"
!
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!
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.
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
.
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.
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.
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.
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.
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!
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.
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.
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.
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!
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.
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.
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.
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.
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.
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.
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.
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.
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.
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!
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?
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.
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?
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
.
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.
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.
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.
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.
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.
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?
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.
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.
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.
"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 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!
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.
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.
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?
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.
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?
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.
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.
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
.
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.
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!
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!
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?
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?
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.
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.
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.
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.
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.
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!
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.
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.
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!
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!
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.
First of all we'll need to install Ruby.
We have several tools on each major platform to install Ruby:
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).
Terminal
and type irb
, then hit enter.irb
and hit enter.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