case and when in depth
What when compares
case takes a value and asks each when whether it matches. The comparison is not ==; it is ===, and each class defines it for a useful kind of match. That is why a when can hold a range or a class and still read like a sentence.
ruby -e 'case 7; when 1..5 then puts :low; else puts :high; end'
high
7 does not fall in 1..5, so the else branch runs. then is optional; it lets the body sit on the same line as the when, which is what the one-liners use.
Ranges, classes, regexes, lambdas
Four kinds of match cover most real code. A class matches any instance of it:
ruby -e 'case "x"; when Integer then puts :int; when String then puts :str; end'
str
A regex matches a string that contains the pattern:
ruby -e 'case "error 500"; when /5\d\d/ then puts :server_error; else puts :ok; end'
server_error
A lambda works because Proc defines === as a call, so any predicate can be a when:
ruby -e 'even = ->(n) { n.even? }; case 4; when even then puts :even; else puts :odd; end'
even
Ranges, classes and regexes are all objects with a ===; a lambda simply calls its body. Once that is clear, a case with a dozen branches stays readable.
The missing else
A case without a matching when and without an else returns nil and carries on. It does not raise, which is how a value falls through a long list without a word:
ruby -e 'case 9; when 1 then puts :one; end; puts "after"'
after
When the list is meant to cover every case, write the else and raise there. A missing branch then becomes an error with the value in the message, instead of a nil that surfaces three methods later.
case in as a first taste
Ruby 3 adds a second form, case/in, for matching the shape of a value rather than its identity:
ruby -e 'case {status: "ok", code: 200}; in {status: "ok", code: code}; puts code; end'
200
The in pattern matches a hash with those keys and binds code to the value. Lesson 37, later in the course, covers the whole form; the point here is that case is the same keyword, with a stricter matcher.
What to watch for
Order matters: the first matching when wins, so a
broad range before a narrow one hides the narrow case. Put the specific
patterns first, or the general one last.
A case on a class name compares the class, not
the value: when Integer matches any integer, and
when 1 matches only one. Mixing the two in one
list is fine as long as the order is right.
Next: Guard clauses and modifiers
Article author: Arthur Isaev