ruby.onl / secret-operators

case/when: The Switch Statement That Actually Delivers

2026-03-19

Ruby's case/when uses the === operator for matching, which behaves differently depending on the type. This makes it absurdly more powerful than a simple equality check. Regex patterns, ranges, classes, lambdas: they all work as conditions. Perl's deprecated given/when never even came close.

Part 1: Basic Usage

case line when %r~^ERROR~ then handle_error(line) # regex match when %r~^WARN~ then handle_warn(line) when "" then next # empty string when 1..100 then puts "in range" # range membership when Integer then puts "it's a number" # class check when ->(x) { x.size > 100 } then puts "long" # lambda/proc end
One statement. Six completely different types of matching. All using === under the hood.

Part 2: What === Does for Each Type

Type What === Does Example
Regex =~ match %r~error~ === "error log" => true
Range include? (1..10) === 5 => true
Class is_a? String === "hello" => true
Proc/Lambda call ->(x){x>5} === 10 => true
String == equality "hello" === "hello" => true
Symbol == equality :error === :error => true

Part 3: Lambdas as Smart Matchers

Since Proc#=== calls the proc, you can use lambdas as conditions in case statements:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Create reusable conditions # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ multiple_of = ->(factor) { ->(n) { (n % factor).zero? } } case number when multiple_of[3] then puts "Multiple of 3" when multiple_of[7] then puts "Multiple of 7" when multiple_of[11] then puts "Multiple of 11" end
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Day-of-week matching with lambdas # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ is_weekend = ->(date) { [0, 6].include?(date.wday) } is_monday = ->(date) { date.wday == 1 } case Time.now when is_weekend then puts "Weekend!" when is_monday then puts "Monday..." else puts "Weekday" end

Part 4: Case Without Expression

When you use case without an expression, it acts like an if/elsif chain:
case when line.empty? next when line =~ %r~^#~ next # skip comments when line.length > 1000 warn "line too long at #{$.}" else process(line) end

Part 5: Case as Expression

Ruby's case returns a value, so you can assign the result:
severity = case line when %r~CRITICAL|FATAL~i then :critical when %r~ERROR~i then :error when %r~WARN~i then :warning when %r~INFO~i then :info when %r~DEBUG~i then :debug else :unknown end counts[severity] += 1
This is way cleaner than a chain of if/elsif blocks that all assign to the same variable. The intent is obvious: classify and assign.

Created By: Wildcard Wizard. Copyright 2026