Control Flow

Ruby Fundamentals — Control Flow
Ruby Fundamentals
Course 1 · Chapter 3 · Control Flow

🔀 Control Flow

Chapter 2 established that only nil and false are falsy in Ruby — everything else, including 0 and "", is truthy. This chapter puts that rule to work in if, case, and loops, and introduces the idea that will keep resurfacing for the rest of this course: in Ruby, control flow structures aren't just statements — they're expressions that return a value.

❓ if / elsif / else

age = 20 if age < 13 puts "child" elsif age < 20 puts "teenager" else puts "adult" end

No parentheses around the condition, no curly braces around the body — Ruby uses indentation for readability (like Python) but the block is actually delimited by the end keyword (like PHP's alternate if: ... endif; syntax, though that form is rarely used in real PHP code). Note also elsif — not else if (PHP) or elif (Python), a small but very common early typo.

The modifier form

For a single statement, Ruby lets you write the condition after the code — a postfix form with no real equivalent in PHP or Python:

puts "You can vote" if age >= 18 puts "Still a minor" unless age >= 18

🙅 unless — Ruby's if not

unless condition runs its body when the condition is falsy — the direct opposite of if. Neither PHP nor Python has a dedicated keyword for this; both just write if (!condition) or if not condition. Ruby style strongly prefers unless over if ! when there's no accompanying else, because it reads closer to plain English:

unless user.nil? puts "Welcome back!" end # Avoid: unless ... else ... end -- it reads backwards and is discouraged style

💡 Ruby Is Expression-Oriented

This is the chapter's central idea: in Ruby, if and case evaluate to the value of whichever branch ran, so you can assign the result directly instead of declaring a variable first and mutating it inside each branch:

temperature = 75 description = if temperature > 80 "hot" elsif temperature > 60 "warm" else "cold" end puts description # => warm

Assigning a Conditional Result: PHP vs Python vs Ruby

PHPPythonRuby
ApproachTernary only: $x = $a ? $b : $c;Conditional expression: x = b if a else cFull if/elsif/else block IS an expression
Multi-branch?Needs nested ternaries (unreadable) or a temp variableNeeds nested conditional expressions or a temp variableAny number of elsif branches, still one clean assignment

Ruby still has a ternary operator too (age >= 18 ? "adult" : "minor"), useful for short one-liners — but for anything with more than two branches, an expression-oriented if avoids the unreadable nested-ternary problem PHP and Python code sometimes falls into.

🔁 case / when

Ruby's answer to PHP's switch (Python only gained a comparable match statement in 3.10) — and, being Ruby, it's an expression too:

grade = "B" message = case grade when "A" then "Excellent" when "B", "C" then "Good" when "D" then "Needs improvement" else "Not a valid grade" end puts message # => Good

when "B", "C" matches either value with no fall-through logic needed — Ruby's case never falls through to the next branch the way PHP's switch does without an explicit break. case can also match against a Range or a class, which is worth knowing exists even before classes are formally covered:

score = 85 case score when 90..100 then puts "A" when 80...90 then puts "B" # matches -- 85 is in this range else puts "Below B" end

🔂 Loops — while, until, for, and .each

i = 0 while i < 3 puts i i += 1 end i = 0 until i == 3 # the exact opposite of while -- no PHP/Python keyword equivalent puts i i += 1 end for n in 1..3 # for exists, but idiomatic Ruby rarely uses it -- see below puts n end
⚠ for exists, but real Ruby code almost never uses it

Unlike PHP and Python, where for loops are completely normal and common, idiomatic Ruby strongly prefers iterator methods like .each over the for keyword. It's not a hard rule, but seeing a bare for loop in Ruby code is a signal the author is likely coming from another language and hasn't adopted Ruby style yet. The next section shows why.

[1, 2, 3].each do |n| puts n end # Same thing, curly-brace block syntax -- idiomatic for short one-liners [1, 2, 3].each { |n| puts n }

Why .each over for

A Ruby for loop doesn't create its own scope — the loop variable leaks out and stays defined after the loop ends, which most Ruby developers consider a wart carried over from C-style languages. .each is a method call that takes a block, and the block variable stays cleanly scoped to the block itself.

do...end vs { }

Both delimit a block; the convention is do...end for multi-line blocks and { } for short one-liners. This is purely a style convention, not a functional difference — but following it is part of what makes Ruby code look idiomatic rather than translated from another language.

📜 Control Flow Keywords, Side by Side

PHP vs Python vs Ruby

ConceptPHPPythonRuby
Negative-else keywordnone (if !)none (if not)unless
Switch/matchswitch (needs break)match (3.10+)case/when (no fall-through)
Reverse-while loopnonenoneuntil
Idiomatic iterationforeachfor x in list.each do |x| ... end
if/case as expressionNo (ternary only)Partial (conditional expr only)Yes, fully

💻 Coding Challenges

Challenge 1: Modifier if/unless

Given an integer variable stock, write two lines using the modifier form: one that prints "In stock" using if, and one that prints "Reorder soon" using unless. Test both with a value above and below 10.

Goal: Get comfortable reading and writing Ruby's postfix conditional style.

→ Solution

Challenge 2: case/when as an Expression

Given an integer day_number (1–7), use case/when to assign the corresponding day name directly to a variable called day_name (no separate puts inside each branch) — then print day_name once, after the case block.

Goal: Practice treating case as an expression rather than a sequence of side-effecting branches.

→ Solution

Challenge 3: for vs .each

Write the same task two ways: print the numbers 1 through 5 using a for loop, then print them again using .each on a range ((1..5).each). Add a comment noting which version is the idiomatic Ruby choice and why.

Goal: Feel the stylistic difference firsthand, not just read about it.

→ Solution

💎 Expression-Oriented Code Reduces Temp Variables

Once if and case return values, a whole category of PHP/Python pattern — declare an empty variable, then reassign it inside every branch of a conditional — mostly disappears from idiomatic Ruby. Get in the habit of asking "can I just assign this directly?" before reaching for the declare-then-mutate pattern out of PHP/Python habit.

🎯 What's Next

Next chapter: Methods & Blocks — defining methods, default and keyword arguments, Ruby's implicit return, and a first proper look at blocks and yield.