Ruby Control Flow Explained — if unless case when Ternary and Truthiness
In this tutorial, you will learn about Ruby Control Flow Explained. We cover key concepts, practical examples, and best practices to help you master this topic.
Ruby control flow includes if/unless, case/when, and ternary operators with unique truthiness rules where only nil and false are falsy values, making conditional logic expressive and readable.
What You'll Learn
- Using if, unless, and elsif for conditionals
- The case/when statement with multiple conditions
- Ternary operators for inline conditions
- Ruby's truthiness rules and how they differ from other languages
- Modifier forms for concise conditional code
Why It Matters
Control flow is how programs make decisions. In security tools like Durga Antivirus Pro, control flow determines whether a file is flagged as malicious based on signature matches and behavior patterns. Doda Browser uses control flow to manage user preferences, tab states, and network requests. Mastering Ruby's expressive conditionals means writing cleaner, more readable decision logic.
Real-World Use
A Rails controller might use case/when to handle different HTTP response formats, if/unless for authorization checks, and ternary for quick conditional assignments in views. Understanding Ruby's unique syntax makes your code more idiomatic and concise.
flowchart LR
A["Control Flow"] --> B["if/unless"]
B --> C["case/when"]
C --> D["Ternary"]
D --> E["Loops"]
A:::current --> B
style A fill:#2563eb,stroke:#2563eb,color:#fff
style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b
If Statement
The basic conditional in Ruby:
age = 18
if age >= 18
puts "You can vote"
end
# You can vote
If/Else
temperature = 30
if temperature > 25
puts "It's hot outside"
else
puts "It's cool outside"
end
# It's hot outside
If/Elsif/Else
Ruby uses elsif (no second 'e'):
score = 85
if score >= 90
puts "Grade: A"
elsif score >= 80
puts "Grade: B"
elsif score >= 70
puts "Grade: C"
else
puts "Grade: D"
end
# Grade: B
Unless
unless is the opposite of if. It executes when the condition is falsy:
age = 16
unless age >= 18
puts "You are under 18"
end
# You are under 18
# Equivalent to:
if !(age >= 18)
puts "You are under 18"
end
Unless/Else
is_admin = false
unless is_admin
puts "Access denied"
else
puts "Welcome admin"
end
# Access denied
The Modifier Forms
Ruby allows conditionals at the end of a line for concise code:
puts "Adult" if age >= 18
puts "Minor" unless age >= 18
# Equivalent to:
if age >= 18
puts "Adult"
end
Ternary Operator
The ternary operator is a compact if/else:
age = 20
status = age >= 18 ? "adult" : "minor"
puts status
# adult
# Same as:
if age >= 18
status = "adult"
else
status = "minor"
end
You can nest ternaries (but avoid for readability):
score = 85
grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D"
puts grade
# B
Case/When
Ruby's case/when is more flexible than switch statements in other languages.
Basic Case/When
day = "Monday"
case day
when "Monday"
puts "Start of work week"
when "Friday"
puts "Almost weekend"
when "Saturday", "Sunday"
puts "Weekend!"
else
puts "Midweek"
end
# Start of work week
Case with Ranges
score = 85
case score
when 90..100
puts "Grade A"
when 80...90
puts "Grade B"
when 70...80
puts "Grade C"
else
puts "Grade D"
end
# Grade B
Note: .. includes the end value, ... excludes it.
Case with Procs and Lambdas
grade = "B"
case grade
when ->(g) { %w[A B C].include?(g) }
puts "Passing"
when "D"
puts "Conditional pass"
else
puts "Failing"
end
# Passing
Case Without an Expression
You can use case without a target value for clean boolean checks:
age = 25
case
when age < 13
puts "Child"
when age < 18
puts "Teenager"
when age < 65
puts "Adult"
else
puts "Senior"
end
# Adult
Truthiness in Ruby
Ruby's truthiness is simple but different from many languages:
# Only false and nil are falsy
puts "truthy" if true # truthy
puts "truthy" if 1 # truthy
puts "truthy" if 0 # truthy (surprising to some!)
puts "truthy" if "" # truthy (surprising!)
puts "truthy" if [] # truthy
puts "truthy" if "false" # truthy (non-empty string)
puts "falsy" if false # (nothing)
puts "falsy" if nil # (nothing)
# Checking truthiness
puts !!true # true
puts !!0 # true
puts !!nil # false
puts !!false # false
Logical Operators
# && — returns first falsy value or last truthy
puts nil && 42 # nil
puts 0 && 42 # 42 (0 is truthy in Ruby!)
puts "hello" && 42 # 42
# || — returns first truthy value or last falsy
puts nil || 42 # 42
puts 0 || 42 # 0
puts "hello" || 42 # "hello"
Conditional Assignment
Ruby has several conditional assignment operators:
# ||= — assign if nil or false
name = nil
name ||= "Default"
puts name # "Default"
# &&= — assign if truthy
status = true
status &&= "active"
puts status # "active"
# ||= is commonly used for memoization
def cached_data
@data ||= expensive_calculation
end
Inline If/Unless Modifier Best Practices
Use modifier form for simple, single-line conditions:
# Good — simple and clear
puts "Over limit" if count > 100
raise "Invalid" unless valid?
# Bad — too complex for modifier form
puts "Processing completed successfully after #{duration} seconds with #{errors} errors" if count > 100 && duration > 0 && errors == 0
Common Mistakes
1. Using = Instead of == in Conditions
# Wrong — assigns, doesn't compare
if x = 5
puts "x is 5" # This always runs!
end
# Right
if x == 5
puts "x is 5"
end
2. Forgetting nil and false Are the Only Falsy Values
# This works in many languages but not Ruby
if 0
puts "This WILL run in Ruby" # 0 is truthy!
end
3. Using Unless with Complex Conditions
# Hard to read
unless !user.nil? && user.active? && user.admin?
puts "Access denied"
end
# Better
if user.nil? || !user.active? || !user.admin?
puts "Access denied"
end
4. Forgetting elsif Spelling
# Wrong
if x > 0
puts "positive"
else if x < 0 # This creates nested if, not elsif
puts "negative"
end
# Right
if x > 0
puts "positive"
elsif x < 0
puts "negative"
end
5. Overusing Ternary for Complex Logic
# Bad — impossible to read
result = a > b ? (c > d ? x : y) : (e > f ? z : w)
# Better
if a > b
result = c > d ? x : y
else
result = e > f ? z : w
end
Practice Questions
1. What's the difference between if and unless?
if executes when the condition is truthy. unless executes when the condition is falsy. unless age >= 18 is equivalent to if !(age >= 18).
2. What values are truthy in Ruby?
Everything except false and nil. This includes 0, "", [], {}, and "false".
3. How do you write a one-line conditional in Ruby?
Using the modifier form: puts "Adult" if age >= 18 or raise "Error" unless valid?.
4. What does ||= do?
It assigns a value only if the variable is nil or false. Commonly used for memoization: @cache ||= compute_value.
Challenge: Write a Ruby program that asks for a score (0-100) and prints the letter grade using case/when. Include edge case handling for scores outside the valid range.
Solution
print "Enter your score (0-100): "
score = gets.chomp.to_i
grade = case score
when 90..100 then "A"
when 80...90 then "B"
when 70...80 then "C"
when 60...70 then "D"
when 0...60 then "F"
else "Invalid score"
end
puts "Grade: #{grade}"
# Additional feedback
case grade
when "A"
puts "Excellent work!"
when "B"
puts "Good job!"
when "C"
puts "Fair effort"
when "D"
puts "Needs improvement"
when "F"
puts "Please try again"
end
FAQ
{{< faq question="Why does Ruby use elsif instead of else if or elif?" >}}
Ruby's creator Matz wanted to minimize punctuation. elsif is a single word without extra braces or keywords. It's consistent with Ruby's philosophy of reducing visual noise in code.
{{< /faq >}}
{{< faq question="Can I use case/when with any object type?" >}}
Yes. Case/when uses === (the case equality operator) for comparison. Each class can define its own === behavior. Ranges use inclusion, classes use is_a?, and procs use call semantics.
{{< /faq >}}
{{< faq question="Should I use unless/else or rewrite as if/else?" >>
Use unless when the condition is negative and simple. Avoid unless with else — it becomes confusing. unless x is clear; unless x ... else ... should be rewritten as if x ... else .... `{{< /faq >}}
{{< faq question="What's the difference between .. and ... in ranges?" >}}
.. includes the end value (1..5 = 1,2,3,4,5). ... excludes the end value (1...5 = 1,2,3,4). This matters in case/when with ranges.
{{< /faq >}}
| {{< faq question="Can I use and/or instead of &&/ | ?" >}} |
|---|
{{< /faq >}}
Try It Yourself
Run this comprehensive control flow demo:
# control_flow_demo.rb
def check_access(user, resource)
if user.nil?
return "No user provided"
end
unless user[:active]
return "User account is inactive"
end
case resource[:level]
when :admin
user[:role] == :admin ? "Access granted" : "Access denied — admin only"
when :user
"Access granted"
else
"Unknown resource level"
end
end
admin = { name: "Alice", role: :admin, active: true }
regular = { name: "Bob", role: :user, active: true }
inactive = { name: "Charlie", role: :user, active: false }
puts check_access(admin, { level: :admin })
puts check_access(regular, { level: :admin })
puts check_access(inactive, { level: :user })
puts check_access(nil, { level: :user })
Expected output:
Access granted
Access denied — admin only
User account is inactive
No user provided
What's Next
Now that you can control program flow, learn about loops and iteration to repeat actions efficiently.
| Topic | Description | Link |
|---|---|---|
| Ruby Loops | each, while, until, times | {{< ref "05-loops" >}} |
| Ruby Arrays | Array methods and iteration | {{< ref "06-arrays" >}} |
| Python Control Flow | Compare with Python conditionals | Python |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro