---
title: "Ruby's case statement - advanced techniques"
published: "2015-07-15"
publisher: Honeybadger
author: Starr Horne
category: Ruby articles
tags:
  - Ruby
description: "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."
url: "https://www.honeybadger.io/blog/rubys-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 at just one example:

```ruby
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.

```ruby
# 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](http://ruby-doc.org/core-2.2.0/Range.html#method-i-3D-3D-3D).

```ruby
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](http://ruby-doc.org/core-2.2.0/Regexp.html#method-i-3D-3D-3D) for `Regexp` explain this.

```ruby
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](http://ruby-doc.org/core-2.2.0/Proc.html#method-i-3D-3D-3D) for it. What this means is that you can use lambdas and procs in your case statement as dynamic matchers.

```ruby
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:

```ruby
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
```

---

## Try Honeybadger for FREE

Intelligent logging, error tracking, and Just Enough APM™ in one dev-friendly platform. Find and fix problems before users notice.

[Start free trial](https://app.honeybadger.io/users/sign_up)

[See plans and pricing](https://www.honeybadger.io/plans/)
