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:
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
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
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
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
Persons by defining the
<=> method for the
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:
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)
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
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
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
condition ? do_this_if_true : do_this_if_false
> 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 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
option_a, do this, if it equals
option_b, do this, if it equals
option_c, do this... and otherwise do this."
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:
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_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).