CrackedRuby logo

CrackedRuby

Ternary Operator

Overview

The ternary operator (? :) provides a compact way to write simple conditional expressions in Ruby. It evaluates a boolean condition and returns one of two values based on the result. The ternary operator follows the syntax condition ? value_if_true : value_if_false and serves as a concise alternative to simple if-else statements.

Ruby's ternary operator is an expression that always returns a value, making it useful for variable assignments, method arguments, and return statements where you need conditional logic in a single line.

# Basic ternary operator usage
age = 20
status = age >= 18 ? "adult" : "minor"
puts status  # => "adult"

# Equivalent if-else statement
if age >= 18
  status = "adult"
else
  status = "minor"
end

The ternary operator has the same precedence as the || and && operators and is right-associative, meaning nested ternary operators are evaluated from right to left.

Basic Usage

Simple Conditional Assignment

The most common use case involves assigning different values to a variable based on a condition:

temperature = 75
weather = temperature > 80 ? "hot" : "comfortable"

score = 85
grade = score >= 90 ? "A" : score >= 80 ? "B" : "C"

user_role = current_user.admin? ? "administrator" : "user"

Method Arguments and Return Values

The ternary operator works well for conditional method arguments and return values:

def greet(name, formal = false)
  greeting = formal ? "Good day" : "Hi"
  "#{greeting}, #{name}!"
end

def calculate_discount(amount, is_member)
  amount * (is_member ? 0.9 : 1.0)
end

def format_price(cents)
  cents > 0 ? "$#{cents / 100.0}" : "Free"
end

Inline Conditionals in String Interpolation

Ternary operators can be embedded within string interpolation for dynamic content:

items_count = 3
message = "You have #{items_count} item#{items_count == 1 ? '' : 's'} in your cart"

logged_in = true
nav_link = "#{logged_in ? 'Dashboard' : 'Login'}"

user = { name: "Alice", premium: true }
badge = "#{user[:name]}#{user[:premium] ? '' : ''}"

Hash and Array Operations

Use ternary operators for conditional hash values or array operations:

config = {
  environment: Rails.env.production? ? "prod" : "dev",
  debug: Rails.env.development? ? true : false,
  timeout: high_traffic ? 30 : 60
}

# Conditional array filtering
results = search_term.empty? ? all_records : filtered_records

Advanced Usage

Nested Ternary Operators

While nested ternary operators can handle multiple conditions, use them judiciously to maintain readability:

# Traffic light logic
def traffic_action(light_color)
  light_color == "green" ? "go" :
    light_color == "yellow" ? "caution" :
      light_color == "red" ? "stop" : "unknown"
end

# Grade calculation with multiple thresholds
def letter_grade(score)
  score >= 97 ? "A+" :
    score >= 93 ? "A" :
      score >= 90 ? "A-" :
        score >= 87 ? "B+" :
          score >= 83 ? "B" : "Below B"
end

# Complex conditional logic
def pricing_tier(user)
  user.premium? ?
    (user.annual? ? "premium_annual" : "premium_monthly") :
    (user.trial? ? "trial" : "basic")
end

Method Chaining with Ternary Operators

Ternary operators can be combined with method chaining for concise conditional operations:

# Conditional method chaining
text = input.present? ? input.strip.downcase.capitalize : "Default"

# API response formatting
response = success ? data.to_json : { error: message }.to_json

# Conditional transformation pipeline
result = valid_email? ?
  email.downcase.strip.gsub(/\s+/, '') :
  default_email

Lambda and Proc Integration

Ternary operators work effectively with lambdas and procs for dynamic behavior:

# Conditional lambda selection
sorter = ascending ? ->(a, b) { a <=> b } : ->(a, b) { b <=> a }
sorted_array = array.sort(&sorter)

# Dynamic validation
validator = strict_mode ?
  ->(value) { value.present? && value.length >= 8 } :
  ->(value) { value.present? }

# Conditional callback assignment
callback = success ? method(:on_success) : method(:on_failure)

Class and Module Context

Use ternary operators in class definitions and module contexts:

class User
  ROLE_MAPPINGS = Rails.env.production? ?
    { admin: 1, user: 2, guest: 3 } :
    { admin: 1, user: 2, guest: 3, developer: 0 }

  def initialize(name, admin = false)
    @name = name
    @permissions = admin ? [:read, :write, :delete] : [:read]
  end

  def display_name
    @name.length > 20 ? "#{@name[0..17]}..." : @name
  end
end

Common Pitfalls

Operator Precedence Issues

The ternary operator has specific precedence rules that can cause unexpected behavior:

# Problematic: assignment has lower precedence
result = condition ? a = "yes" : a = "no"  # Confusing
# Better: use parentheses
result = condition ? (a = "yes") : (a = "no")
# Best: separate assignment
a = condition ? "yes" : "no"
result = a

# Method call precedence
# This doesn't work as expected:
puts condition ? "true" : "false".upcase  # => "false" or "FALSE"
# Should be:
puts condition ? "true" : ("false".upcase)
puts (condition ? "true" : "false").upcase

Nil and Falsy Values

Ruby's truthiness rules apply to ternary operators - only nil and false are falsy:

# Common mistake with zero, empty strings, empty arrays
count = 0
message = count ? "items available" : "no items"  # => "items available"
# Should check for zero specifically:
message = count > 0 ? "items available" : "no items"

# Empty collections are truthy
array = []
status = array ? "has data" : "empty"  # => "has data"
# Check for emptiness:
status = array.any? ? "has data" : "empty"

# Nil vs false distinction
def process_flag(flag)
  # This treats nil and false the same:
  flag ? "enabled" : "disabled"

  # If nil means "not set" and false means "explicitly disabled":
  flag.nil? ? "not set" : (flag ? "enabled" : "disabled")
end

Readability and Maintainability

Complex ternary operations can become difficult to read and maintain:

# Hard to read and debug
result = condition1 ? (condition2 ? value1 : condition3 ? value2 : value3) : value4

# Better: use explicit if-else for complex logic
if condition1
  if condition2
    result = value1
  elsif condition3
    result = value2
  else
    result = value3
  end
else
  result = value4
end

# Or extract to a method
def determine_result
  return value4 unless condition1
  return value1 if condition2
  return value2 if condition3
  value3
end

Side Effects in Ternary Expressions

Be cautious with expressions that have side effects in ternary operators:

# Problematic: both sides have side effects
result = condition ? increment_counter : decrement_counter

# Both methods get defined and could be called unexpectedly
# Better: use explicit conditional
if condition
  increment_counter
else
  decrement_counter
end

# File operations example
# Dangerous:
file = writable ? File.open("log.txt", "w") : File.open("log.txt", "r")
# Better:
mode = writable ? "w" : "r"
file = File.open("log.txt", mode)

Performance Considerations

Short-Circuit Evaluation

Ternary operators use short-circuit evaluation, only executing the chosen branch:

# Expensive operations are only called when needed
result = cache_hit? ? cached_value : expensive_calculation

# Database queries
user = logged_in? ? User.find(session[:user_id]) : nil

# File system operations
content = file_exists? ? File.read(path) : default_content

Memory Allocation Patterns

Consider memory allocation when using ternary operators with object creation:

# Both objects created regardless of condition (avoid)
result = condition ? expensive_object_a : expensive_object_b

# Better: only create needed object
result = if condition
  expensive_object_a
else
  expensive_object_b
end

# String allocation example
# Creates both strings:
message = error ? "Error: #{details}" : "Success: #{details}"
# Better for memory:
prefix = error ? "Error" : "Success"
message = "#{prefix}: #{details}"

Testing Strategies

Unit Testing Ternary Logic

Test both branches of ternary operators to ensure complete coverage:

RSpec.describe "user status" do
  it "returns adult for users 18 and older" do
    user = User.new(age: 18)
    status = user.age >= 18 ? "adult" : "minor"
    expect(status).to eq("adult")
  end

  it "returns minor for users under 18" do
    user = User.new(age: 17)
    status = user.age >= 18 ? "adult" : "minor"
    expect(status).to eq("minor")
  end

  it "handles edge case at exactly 18" do
    user = User.new(age: 18)
    status = user.age >= 18 ? "adult" : "minor"
    expect(status).to eq("adult")
  end
end

Mocking Conditional Dependencies

Use mocking to test different ternary operator branches:

describe "pricing calculation" do
  let(:user) { double("user") }

  context "when user is premium" do
    before { allow(user).to receive(:premium?).and_return(true) }

    it "applies premium discount" do
      price = user.premium? ? 100 * 0.8 : 100
      expect(price).to eq(80)
    end
  end

  context "when user is not premium" do
    before { allow(user).to receive(:premium?).and_return(false) }

    it "applies no discount" do
      price = user.premium? ? 100 * 0.8 : 100
      expect(price).to eq(100)
    end
  end
end

Integration Testing Complex Conditionals

Test ternary operators within the context of larger workflows:

feature "conditional navigation" do
  scenario "admin user sees admin links" do
    admin_user = create(:user, :admin)
    login_as(admin_user)

    visit dashboard_path

    # Test ternary logic in view:
    # <%= current_user.admin? ? "Admin Panel" : "User Dashboard" %>
    expect(page).to have_content("Admin Panel")
    expect(page).not_to have_content("User Dashboard")
  end

  scenario "regular user sees standard links" do
    regular_user = create(:user)
    login_as(regular_user)

    visit dashboard_path

    expect(page).to have_content("User Dashboard")
    expect(page).not_to have_content("Admin Panel")
  end
end

Reference

Syntax and Structure

Component Description Example
condition Boolean expression to evaluate x > 5, user.admin?, array.empty?
? Separator between condition and true value Required operator
value_if_true Expression returned when condition is truthy "yes", calculate(), object.method
: Separator between true and false values Required operator
value_if_false Expression returned when condition is falsy "no", default_value, nil

Operator Precedence

Precedence Level Operators Associativity
Higher [], ., :: Left to right
** Right to left
+, - (unary) Right to left
*, /, % Left to right
+, - Left to right
<<, >> Left to right
& Left to right
|, ^ Left to right
>, >=, <, <=, <=>, ==, ===, !=, =~, !~ Left to right
&& Left to right
|| Left to right
.., ... Non-associative
Current ? : Right to left
Lower =, +=, -=, etc. Right to left

Common Patterns

Pattern Use Case Example
Simple Assignment Variable initialization status = online? ? "active" : "inactive"
Method Arguments Conditional parameters format_date(date, verbose ? :long : :short)
Return Values Method returns def max(a, b); a > b ? a : b; end
String Interpolation Dynamic content "#{count} item#{count == 1 ? '' : 's'}"
Hash Values Configuration objects { timeout: production? ? 30 : 5 }
Validation Input checking age = input.to_i > 0 ? input.to_i : 18
Nil Safety Default values name = user&.name ? user.name : "Anonymous"

Truthiness Reference

Value Truthy/Falsy Ternary Result
true Truthy Returns first value
false Falsy Returns second value
nil Falsy Returns second value
0 Truthy Returns first value
"" Truthy Returns first value
[] Truthy Returns first value
{} Truthy Returns first value
Any object Truthy Returns first value

Best Practices Summary

Practice Recommendation Reason
Line Length Keep ternary expressions under 80 characters Improves readability
Nesting Limit Avoid more than 2 levels of nesting Prevents complexity
Side Effects Avoid expressions with side effects Maintains predictability
Parentheses Use parentheses for complex expressions Clarifies precedence
Consistency Use similar patterns across codebase Improves maintainability
Documentation Comment complex ternary logic Aids future developers
Testing Test both branches explicitly Ensures complete coverage