Ruby's case statement - advanced techniques

Nothing could be simpler and more boring than the case statement. It’s a holdover from C. You use it to replace a bunch of ifs. Case closed. Or is it? Actually, case statements in Ruby are a lot richer and more complex than you might imagine. Let’s take a look.

Nothing could be simpler and more boring than the case statement. It's a holdover from C. You use it to replace a bunch of ifs. Case closed. Or is it?

Actually, case statements in Ruby are a lot richer and more complex than you might imagine. Let's take a look at just one example:

case "Hi there"
when String
  puts "case statements match class"
end

# outputs: "case statements match class"

This example shows that case statements not only match an item's value but also its class. This is possible because under the hood, Ruby uses the === operator, aka. the three equals operator.

A quick tour of the === operator

When you write x === y y in Ruby, you're asking "does y belong in the group represented by x?" This is a very general statement. The specifics vary, depending on the kind of group you're working with.

# Here, the Class.===(item) method is called, which returns true if item is an instance of the class 

String === "hello" # true
String === 1 # false


Strings, regular expressions and ranges all define their own ===(item) methods, which behave more or less like you'd expect.  You can even add a triple equals method to your own classes.

Now that we know this, we can do all sorts of tricks with case.

Matching ranges in case statements

You can use ranges in case statements thanks to the fact that range === n simply returns the value of range.include?(n). How can I be so sure? It's in the docs.

case 5
when (1..10)
  puts "case statements match inclusion in a range"
end

# outputs "case statements match inclusion in a range"

Matching regular expressions with case statements

Using regexes in  case statements is also possible, because /regexp/ === "string" returns true only if the string matches the regular expression. The docs for Regexp explain this.

case "FOOBAR"
when /BAR$/
  puts "they can match regular expressions!"
end

# outputs "they can match regular expressions!"

Matching procs and lambdas

This is kind of a weird one. When you use   Proc#===(item), it's the same as doing Proc#call(item). Here are the docs for it. What this means is that you can use lambdas and procs in your case statement as dynamic matchers.

case 40
when -> (n) { n.to_s == "40" }
  puts "lambdas!"
end

# outputs "lambdas"

Writing your own matcher classes

As I mentioned above, adding custom case behavior to your classes is as simple as defining your own === method. One use for this might be to pull out complex conditional logic into multiple small classes. I've sketched out how that might work in the example below:

class Success
  def self.===(item)
    item.status >= 200 && item.status < 300
  end
end

class Empty
  def self.===(item)
    item.response_size == 0
  end
end

case http_response
when Empty
  puts "response was empty"
when Success
  puts "response was a success"
end
What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo

    Starr Horne

    Starr Horne is a Rubyist and Chief JavaScripter at Honeybadger.io. When she's not neck-deep in other people's bugs, she enjoys making furniture with traditional hand-tools, reading history and brewing beer in her garage in Seattle.

    More articles by Starr Horne
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like: