Decisions, and what counts as true
if, elsif and unless, the modifiers that read like English, case for many branches, and Ruby's honest surprise: only nil and false are false.
If, elsif, else
A condition is any expression, and if runs a block when it comes out true: if age >= 18 ... end. The word end closes the block, and there is no rounding bracket around the condition — Ruby trades two braces for two characters of English. elsif adds another question, else catches the rest, and only one branch of the chain ever runs.
unless is if with the question inverted, and it exists because that is sometimes how a person thinks: unless logged_in reads better than if not logged_in. Both forms close with end.
Modifiers, and what is true
A single-statement condition may trail the statement: puts 'adult' if age >= 18. The modifier form is idiomatic when the main action is the point and the condition is a small qualification — next unless ready — and it is one of the places Ruby most obviously reads like English.
Now the surprise that catches every newcomer from another language: only `nil` and `false` are false. Zero is true, an empty string is true, an empty array is true. Ruby asks "is this a value at all", not "would this convert to zero", which means a condition like if count is asking whether count exists — not whether it is positive. Use count > 0 when you mean the number.
Case, and combining questions
case picks one branch from many — no fall-through, no break statements — and its when clauses may list several values or even a range: when 0..12 then 'child'. The then is optional when the body starts on the next line.
Conditions combine with && (and), || (or) and ! (not), and the words and, or, not exist as well with slightly different precedence — the symbols are the ones to reach for in conditions. The ternary condition ? this : that is the one-line version of an if for when both answers are values rather than actions.
Conditions are expressions
if is not a statement that does something to a variable; it is an expression whose value is the last thing the chosen branch evaluated. That is why grade = if score > 90 then 'A' else 'B' end works, and why case can be assigned the same way. The branch that did not run contributes nothing at all.
&& and || do not return true or false; they return one of their operands. nil || 'fallback' gives the string, and name && name.upcase gives the upcased name or nil. They also short-circuit: the right side is not evaluated when the left already decides the answer, which is what makes user && user.name safe.
= assigns and == compares, and Ruby will accept if x = 5 — it assigns, then treats 5 as true, so the branch always runs. The word forms and and or bind more loosely than && and ||, which is why a = b or c assigns b first and evaluates the rest after. And if flag == true is a longer way to ask if flag; the comparison adds a place for a typo without adding meaning.
Every shape of decision
age = 21
if age >= 18
puts 'adult'
elsif age >= 13
puts 'teenager'
else
puts 'child'
end
unless age < 18
puts 'can vote'
end
puts 'working age' if age >= 18 && age < 65
puts 'free entry' if age < 6 || age > 65
# Only nil and false are falsy.
puts 'zero is true' if 0
puts 'empty text is true' if ''
puts 'this never prints' if nil
grade = 'B'
case grade
when 'A' then puts 'excellent'
when 'B', 'C' then puts 'good'
else puts 'keep going'
end
case age
when 0..12 then puts 'child'
when 13..19 then puts 'teenager'
else puts 'adult'
end
puts age >= 18 ? 'yes' : 'no'A ticket desk
Run it and follow which branch each visitor takes. Then add a rule of your own — under fives go free — and a case statement that turns a letter grade into a sentence.
The order of the questions
An if/elsif chain stops at the first true question, so the order is part of the logic. Put the narrowest test first: if age < 13 before if age < 65, or a twelve-year-old is answered by the broader branch and the child branch never runs. This is the same rule as Python’s ladder, and it is the one that produces wrong answers rather than errors.
When a chain gives a surprising answer, print the value and each comparison in order. The first true is the branch that ran, and everything below it was never asked.
Truthiness is about existence
Because only nil and false are false, a condition is really asking whether a value is there. That is convenient for optional data — if name means "a name was supplied" — and wrong for numbers, where zero is a perfectly good value. The fix is an explicit comparison, which also tells the reader which question you meant.
The same rule explains why && and || return operands rather than booleans: they are choosing a value, not answering a yes-or-no question. name || "anonymous" is a default, not a test.
Because zero is true, if quantity is the wrong test for "are there any left" — it is true even when quantity is 0. Write if quantity > 0 and the code says what you mean, in a form that also survives being read at midnight.
Try these next
- Turn a numeric score into a letter with
case, including a branch for a missing score. - Write a one-line modifier that prints a message only when a balance is below zero.
- Evaluate
nil || 0andfalse || 'x'withp, and explain each answer.
Parcel bands
Run it and match each weight to its band. Then add a weight of your own, and change the express rule so it only applies above one kilogram.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
Which values are false in a condition?
