These free mini-courses will give you a strong foundation in web development. Track your progress and access advanced courses on HTML/CSS, Ruby and JavaScript for free inside our student portal.
Scroll down...
Think about how the Ruby interpreter moves through your code. Sometimes you want it to execute a certain chunk of code, other times you don't. In this lesson, you'll see the different ways of controlling the flow of your program.
To create conditions to control your program's flow, you first need to understand which types of things Ruby considers "true" and which ones it considers "false". "Truthiness" and "Falsiness" are ways of saying "what evaluates to true in a conditional statement?" and "what evaluates to false in a conditional statement"? In many languages, there is some nuance to that question.
In Ruby, it's simple: nil
and false
are false and that's it. Everything else is "truthy". It's a very truthy language.
That doesn't mean that these items are actually equivalent to the boolean value true
(they of course have values of their own)... but if you put them in a statement looking for truth, you'll get it. Weird? Keep reading.
# They don't actually equal true...
> 1 == true
#=> false
> 1 == false
#=> false # make up your mind!
> "hello" == true
#=> false
# ...but they evaluate to true
> puts "But now I'm true!" if(1)
But now I'm true!
#=> nil
> puts "you'll never see this" if(nil)
#=> nil
An if
statement is pretty straightforward too. Supply a condition and, if it's true (or truthy!), the code will be executed. If you supply an else
clause, then that code is executed otherwise. Everything proceeds normally from that point forward. Remember that the parentheses are implicit.
if some_variable.is_a?(String)
# do some code if some_variable is a string
else
# this code will not run unless the variable is NOT a String
end
unless
is the opposite of if
(which should make sense from the English of it). So it will jump into the included code... UNLESS the statement is true
.
unless
can also have an else
clause, though it's less common because then you need to scratch your head to think about it so your code gets a bit less readable.
unless home_team.won_the_super_bowl?
puts "I need to drown my sorrows in ice cream"
end
Want some more Ruby awesomeness? Put your if
or unless
statements in a single line:
dispatch_vampire_hunters if current_user.is_a?(Vampire)
send_vampires_home unless still_dark_outside?
Use Comparison Operators as the building blocks to construct your conditional statements. There are some simple ones that you should already be familiar with: ==, <, >, >=, and <=
. !=
is "not equal".
> true == false
#=> false
> true != false
#=> true
The Spaceship Operator <=>
is a special one that comes up because it actually gives three different possible outputs depending on whether the left side is greater than, less than, or equal to the right side.
> 1 <=> 1000
#=> -1
> 1 <=> 1
#=> 0
> 1 <=> -1000
#=> 1
The Spaceship can be useful because, like basically everything else, it's actually a method and you can override it in your own classes.
The Spaceship Operator is most commonly used in sorting methods. Imagine that you created a Person
class and you wanted to sort an array of Person
objects. You first have to teach Ruby how to compare two Person
s by defining the <=>
method for the Person
class:
class Person
attr_accessor :last_name
def initialize(last_name)
@last_name = last_name
end
# To compare two people, use their last names
def <=> (other_person)
self.last_name <=> other_person.last_name
end
end
# IRB
> person1 = Person.new("Smith")
#=> #<Person:0x007fd4e30073b8 @last_name="Smith">
> person2 = Person.new("Jones")
#=> #<Person:0x007fd4e28e98d8 @last_name="Jones">
> people = [person1, person2]
#=> [#<Person:0x007fd4e30073b8 @last_name="Smith">, #<Person:0x007fd4e28e98d8 @last_name="Jones">]
> people.sort # the order gets reversed!
#=> [#<Person:0x007fd4e28e98d8 @last_name="Jones">, #<Person:0x007fd4e30073b8 @last_name="Smith">]
Logical Operators go one step beyond simple comparisons and allow you to start chaining together several comparisons into a single statement. That lets you build more interesting and complex if
statements. The most common are:
&&
aka and
, meaning both sides must be true for the full expression to evaluate to true||
(the pipe symbol, usually on the same key as the backslash) aka or
, meaning that if EITHER of the two sides is true, the expression is true (else false)!
aka not()
, which reverses the expression from true to false or false to true"In the wild" you'll probably see some long, complex or just plain odd looking if
statements. The trick is to start breaking everything that looks like a conditional piece into what it evaluates to... either true
or false
.
So what do you evaluate first? Ruby logical expressions use a similar order of operations to normal math: left to right unless there are parentheses.
> ( false || true ) && !(true && true )
#=> false
Ruby will only evaluate far enough in a logical expression to determine that the expression is definitively true or false.
> puts("this isn't important") && puts("THIS IS IMPORTANT!!!")
"this isn't important"
#=> nil
# we never hit the second puts because
# puts returns nil, which is falsy
# and so Ruby evaluates only the first
# part because it doesn't need the second
# to know that the expression will be false
Ruby will return whatever is returned by the last part of the logical expression to get evaluated. You might expect it to return a simple true
or false
but NO! Ruby is comfortable giving you an actual object instead of true
because that object is guaranteed to be "truthy".
# remember that strings are truthy!
> "I got evaluated!!!" || nil || "I did not :("
#=> "I got evaluated!!!"
> ("I got evaluated!!!" || nil) && "And so did I!"
#=> "And so did I!"
> 7 || nil
#=> 7 # since 7 was the last evaluated expression
That's important because we can actually use methods as part of our logical chains -- the method is run as normal and its return value gets tested for truthiness like any other truthy or falsy value. Sometimes we want to use this lazy evaluation to only run one method depending on the return value of the other:
def falsy_method
puts "falsy because puts returns nil"
end
def truthy_method
puts "getting truthy!"
"truthy because a string, like all else, is truthy"
end
# IRB
> falsy_method && truthy_method
falsy because puts returns nil
#=> nil
> false || truthy_method || falsy_method
getting truthy!
#=> "truthy because a string, like all else, is truthy"
# note that the falsy method never got run
If this seems a bit much to swallow right off the bat, keep it in the back of your mind until you first see it in action then it will click.
Here are some more advanced conditional logic items you're likely to encounter as well.
You may have seen some oddly compact and strange looking statements that appeared to be if
statements under the hood. That's probably because they use the Ternary Operator, which is a shorthand notation for a simple if
that separates the different parts using the ?
and :
like:
condition ? do_this_if_true : do_this_if_false
So:
> true ? puts "I like truth" : puts "not gonna happen"
"I like truth"
#=> nil
You can also nest if
statements inside one another. But if you do, sometimes it gets a little crazy and you find yourself 6 levels deep. You probably need to rethink your strategy, but you might be a candidate for a case
statement.
case
is used for those situations where you're really just checking to see if something equals any one of a number of clear but different options. It basically lets you construct a chain of logic that says
"if x
equals option_a
, do this, if it equals option_b
, do this, if it equals option_c
, do this... and otherwise do this."
For example:
case current_user.energy # Assume it's an integer 1-3
when 3
puts "Go run a marathon!"
when 2
puts "Go for a walk."
when 1
puts "Go take a nap"
else
puts "You're only supposed to have energy of 1,2 or 3..."
end
||=
||=
is a sneaky expression that takes advantage of Ruby's natural laziness -- it basically expands to thing_a || thing_a = thing_b
. So if thing_a
hasn't been set to anything, it becomes thing_b
, otherwise it keeps its original value.
Why? The reasoning is a bit complex and you don't need to know exactly why it works, but we'll go over it for completeness in case you've got a nerd itch that needs scratching:
If thing_a
hasn't yet been assigned to anything, it is nil
and Ruby then checks the right side of the ||
to see if that might be true, which involves running the expression to set thing_a
= thing_b
. If it has already been assigned a value, it just keeps that value like normal. This is another sneaky trick used by programmers in situations like when you don't want to override whatever's already been set, but you want something to be there (like which url originally referred the user to your site).