Control Flow
Reviewed & published by Brayan K
Ruby is a dynamic, beginner-friendly programming language, and control flow is how your program makes decisions — running different code depending on the situation.
Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end of this lesson you'll branch with if/elsif/else, read clearly with unless, match many cases with case/when, and write compact ternaries.
What You'll Learn in This Lesson
- Branching with if, elsif, and else
- Reading conditions naturally with unless
- Trailing (modifier) if/unless for one-liners
- Matching many values with case/when
- case with ranges and the ternary operator
- What truthy and falsy mean in Ruby (only nil and false are falsy)
1️⃣ if, elsif, else, and unless
An if runs its block only when the condition is truthy; elsif adds more tests and else is the fallback. Always close the block with end. Ruby adds unless (the opposite of if) and lets you trail a condition after a statement for tidy one-liners.
# if / elsif / else makes decisions. End the block with "end".
score = 82
if score >= 90
puts "Grade: A"
elsif score >= 80
puts "Grade: B" # -> Grade: B (this branch runs)
elsif score >= 70
puts "Grade: C"
else
puts "Grade: F"
end
# "unless" runs when the condition is FALSE — reads like English.
logged_in = false
unless logged_in
puts "Please sign in" # -> Please sign in
end
# Trailing (modifier) form for one-liners:
puts "Perfect score!" if score == 100 # prints nothing here
puts "Keep trying" unless score >= 90 # -> Keep tryingRemember Ruby's rule: only false and nil are falsy. 0 and "" are both truthy, which surprises newcomers.
2️⃣ case/when and the Ternary
When you're comparing one value against many options, case/when is cleaner. A single when can list several values or match a range. And since if/case are expressions, you can assign their result. For a quick two-way choice, the ternary cond ? a : b is perfect.
# case / when is cleaner than a long if/elsif chain.
day = "Sat"
result = case day
when "Sat", "Sun" # multiple values on one branch
"Weekend!"
when "Mon"
"Ugh, Monday"
else
"A weekday"
end
puts result # -> Weekend!
# case also matches ranges and types:
age = 25
stage = case age
when 0..12 then "child"
when 13..19 then "teen"
when 20..64 then "adult"
else "senior"
end
puts stage # -> adult
# Ternary: condition ? value_if_true : value_if_false
temp = 30
puts temp > 25 ? "hot" : "cool" # -> hotYour turn. Price a movie ticket from an age. Replace the two TODO prices, then run it for a few ages.
# 🎯 YOUR TURN — replace each ___, then run with: ruby ticket.rb
# Decide a movie ticket price based on age.
age = ___ # 👉 replace ___ with 15
price = if age < 5
0 # toddlers free
elsif age < 18
___ # 👉 replace ___ with 8
elsif age >= 65
10 # senior price
else
___ # 👉 replace ___ with 12
end
# Use a ternary to add a label
group = age < 18 ? "minor" : "adult"
puts "Age #{age} (#{group}) pays $#{price}"
# ✅ Expected output (for age 15):
# Age 15 (minor) pays $8Mini-Challenge: FizzBuzz (one number)
The classic interview warm-up, for a single number. Use modulo to test divisibility, and remember to check "both" first. Run with ruby fizz.rb.
# 🎯 MINI-CHALLENGE: FizzBuzz for one number.
# Save as fizz.rb and run with: ruby fizz.rb
#
# Given a number n:
# 1. If it's divisible by BOTH 3 and 5, print "FizzBuzz".
# 2. Else if divisible by 3, print "Fizz".
# 3. Else if divisible by 5, print "Buzz".
# 4. Otherwise print the number itself.
# Hint: use % (modulo). n % 3 == 0 means "divisible by 3".
#
# ✅ Example output (n = 15):
# FizzBuzz
# your code hereCommon Errors (and the fix)
- Missing end — Every if/case block needs a matching end. A "syntax error, unexpected end-of-input" usually means one is missing.
- Using = instead of == — if x = 5 assigns and is always truthy. Use == to compare.
- It's elsif, not elseif — Ruby spells it elsif (no second "e").
- Expecting 0 to be falsy — In Ruby 0 is truthy. To test for zero, write == 0.
- then where it's not needed — On multiline when you don't need then; it's only for one-line branches.
Pro Tips
- 💡 Assign from a branch: label = if x > 0 then "pos" else "neg" end reads cleanly.
- 💡 Combine conditions: use && (and), || (or), and ! (not).
- 💡 Guard clauses: return unless valid at the top of a method keeps the rest unindented.
📋 Quick Reference — Control Flow
| Concept | Ruby Syntax |
|---|---|
| Branch | if / elsif / else / end |
| Negative test | unless cond |
| One-liner | puts x if cond |
| Multi-match | case x; when a, b |
| Ternary | cond ? a : b |
| Logical ops | && || ! |
🎉 Lesson Complete!
- ✅ if/elsif/else branch your code, closed with end
- ✅ unless and trailing modifiers keep simple checks readable
- ✅ case/when matches many values, including ranges
- ✅ The ternary cond ? a : b is a compact two-way choice
- ✅ Only false and nil are falsy in Ruby
Practice quiz
Which keyword runs its block only when the condition is FALSE?
- if
- unless
- while
- until
Answer: unless. unless is the opposite of if — it runs when the condition is false, reading like plain English.
In Ruby, which values are falsy?
- false and nil only
- false, nil, and 0
- false, nil, and ""
- false, nil, 0, and []
Answer: false and nil only. Only false and nil are falsy. 0, "", and [] are all truthy.
What does `case 25; when 0..12 then "child"; when 13..19 then "teen"; else "adult" end` return?
- "child"
- "teen"
- "adult"
- nil
Answer: "adult". 25 falls outside both ranges, so the else branch gives "adult".
How do you spell the 'else-if' keyword in Ruby?
- elseif
- elif
- elsif
- else if
Answer: elsif. Ruby spells it elsif — no second 'e'.
What is the result of the ternary `30 > 25 ? "hot" : "cool"`?
- "hot"
- "cool"
- true
- 30
Answer: "hot". 30 > 25 is true, so the ternary returns the value before the colon: "hot".
What does `grade = if 95 >= 90 then "A" else "B" end` assign to grade?
- nil
- "A"
- "B"
- true
Answer: "A". if is an expression that returns the value of the branch that ran — here "A".
Which operator is used for a single when-branch matching multiple values, e.g. `when "Sat", "Sun"`?
- ==
- ===
- =
- eql?
Answer: ===. case/when uses the === operator under the hood, which is why ranges and multiple values work.
What does `puts "Keep trying" unless 80 >= 90` print?
- nothing
- Keep trying
- an error
- true
Answer: Keep trying. 80 >= 90 is false, so unless runs and prints "Keep trying".
What error usually means an if/case block is missing its closing keyword?
- NoMethodError
- syntax error, unexpected end-of-input
- NameError
- ArgumentError
Answer: syntax error, unexpected end-of-input. Every if/case needs a matching end; a missing one gives 'unexpected end-of-input'.
Using `=` instead of `==` inside `if x = 5` does what?
- Compares x to 5
- Assigns 5 to x and is always truthy
- Raises a syntax error
- Returns nil
Answer: Assigns 5 to x and is always truthy. A single = assigns; `if x = 5` sets x to 5 and is always truthy. Use == to compare.
Continue this course
- Previous: Numbers & Math
- Next: Loops (while, until, loop) — while/until, loop/break, times/upto/step, and next/break/redo
- Quick reference: Ruby cheat sheet › Basics
Frequently asked questions
When should I use unless instead of if?
Use unless when checking that something is NOT the case and a plain if would force you to write if !condition. For example, unless logged_in reads more naturally than if !logged_in. The rule of thumb: prefer unless for a single, simple negative condition. Avoid unless with elsif or complex conditions — an unless/else chain quickly becomes confusing, and a regular if is clearer there.
What counts as 'truthy' and 'falsy' in a Ruby condition?
Ruby is unusually simple here: only two values are falsy — false and nil. Everything else is truthy, including 0, empty strings, and empty arrays. This trips up people coming from other languages where 0 or "" are false. So if count is 0, then if count still runs the if branch. To test for zero specifically, compare explicitly: if count == 0.
Why use case/when instead of a long if/elsif chain?
case/when is more readable when you're comparing one value against many possibilities. It also supports powerful matching: a single when can list several values (when "Sat", "Sun"), match a range (when 0..12), or even match a class (when Integer). Under the hood, when uses the === operator, which is why ranges and classes work. For a handful of unrelated boolean tests, if/elsif is still fine.
Does if return a value in Ruby?
Yes — in Ruby, if, unless, and case are expressions that return the value of whichever branch ran. That's why you can write grade = if score >= 90 then "A" else "B" end and assign the result directly. This is very idiomatic Ruby and avoids repeating the variable name in every branch. If no branch matches and there's no else, the expression returns nil.