Ruby from the ground up

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.

Part 4 of 13

Beginner16 min readBack to Courses

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.

Output
Click "Run" to execute your code

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 || 0 and false || 'x' with p, 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.

Output
Click "Run" to execute your code

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?