The case expression is a multiway conditional. It returns the value of the last expression executed.

There are two forms of case expressions available.

Case expression like if/elsif/else expression style

n = 3
result = 
  case
  when n == 1                 # here you separate the conditional from the result expression with a newline 
    'one'
  when n == 2 then 'two'      # in one-liners you can separate with 'then'
  when n == 3; 'three'        # alternatively, in one-liners you can separate with a semicolon
  when n < 1
    'smaller than one'
  else 'bigger than three'
  end
# => result = 'three'

This is the equivalent if/elsif/else expression:

n = 3
result = 
  if n == 1                  
    'one'
  elsif n == 2 then 'two'
  elsif n == 3; 'three'
  elsif n < 1
    'smaller than one'
  else 'bigger than three'
  end
# => result = 'three'

Once a when clause evaluates to true, no other when clauses are considered.
If no when clause is true and there is no else clause, then the value of the case expression is nil.

n = 4
result = 
  case
  when n == 1 then 'one'
  when n == 2 then 'two'
  when n == 3 then 'three'
  end
# => result = nil

A when clause can have more than one conditional (boolean) expression associated with it. These conditional expressions are separated by commas (,) or the || operator. If any of these conditional expressions evaluate to true, then the code associated with that when is executed.

n = 12
result = 
  case
  when n == 1                  
    'one'
  when n == 2
    'two'      
  when n == 3
    'three'        
  when n == 4, n == 5
    "between four and five"
  when n == 6, n == 7, n == 8, n == 9               # separate conditional expressions with comma
    "between six and nine"
  when n == 10 || n == 11 || n == 12 || n == 13     # alternatively, separate conditional expressions with || operator
    "between ten and thirteen"
  when n < 1
    'smaller than one'
  else 
    'bigger than thirteen'
  end
# => result = 'between ten and thirteen'

Case expression with predefined operand

In this form of the case expression each when clause is tested against a predefined operand. This predefined operand is an expression specified at the top of the case expression after the case keyword.

Each expression of each when clause is tested against the predefined operand using the === case equality operator.

In an abstract notation, the following definition

case <predefined operand>
when <expr1>
  <code1>
when <expr2>
  <code2>
when <expr3>, <expr4>
  <code3>
else
  <code else>
end

is effectively doing this

case 
when <expr1> === <predefined operand>
  <code1>
when <expr2> === <predefined operand>
  <code2>
when <expr3> === <predefined operand> || <expr4> === <predefined operand>
  <code3>
else
  <code else>
end

In the first definition, the predefined (right-hand side) operand was factored out, this makes the notation more compact.
You can list several expressions (left-hand side) to be tested in a when clause. They need to be separated by a comma.
Sometimes the predefined operand is also called the target value.

The last example in if/elsif/else style could be expressed in the predefined operand style like this:

n = 12
result = 
  case n
  when 1                  
    'one'
  when 2
    'two'      
  when 3
    'three'        
  when 4, 5
    "between four and five"
  when 6, 7, 8, 9               # separate expressions with comma
    "between six and nine"
  when 10, 11, 12, 13           # !!! no !!!, this is not allowed: when 10 || 11 || 12 ||  13 
    "between ten and thirteen"
  else
    if n < 1
      'smaller than one'
    else 
      'bigger than thirteen'
    end  
  end
# => result = 'between ten and thirteen'

As you see, this style is less verbose and less repetitive.
Please notice that the final when clause testing n < 1 needs to be included in the else clause.

What is effectively happening could be written in if/elsif/else expression like this:

n = 12
result = 
  if 1 === n                  
    'one'
  elsif 2 === n
    'two'      
  elsif 3 === n
    'three'        
  elsif 4 === n || 5 === n
    "between four and five"
  elsif 6 === n || 7 === n || 8 === n || 9 === n
    "between six and nine"
  elsif 10 === n || 11 === n || 12 === n || 13 === n
    "between ten and thirteen"
  else
    if n < 1
      'smaller than one'
    else 
      'bigger than three'
    end  
  end
# => result = 'between ten and thirteen'

=== case equality operator

Fixnum and Float classes define the === operator like the == operator.
But many other classes, e.g. like Range or Regexp, define the === operator with a special semantic.

Fixnum, Float

1 === 1           # => true
1.0 === 1.0       # => true
1 === 1.0         # => true
1.0 === 1         # => true

String

"hello" === "hello"    # => true
"hello" === "Hello"    # => false

Range

(1..10) === 5     # => true     : 5 is an element of the range (1..10)
(1..10) === 20    # => false    : 20 is not an element of the range (1..10)

Regular Expressions, Regexp

/^prefix/ === "prefix and so on"            # => true
/yes/ === "no"                              # => false
/^[Cc]hapter[ ]*(\d*)/ === "chapter  78"    # => true

Class

NilClass === false         # => false
NilClass === nil           # => true
String === "1"             # => true
String === 1               # => false
Integer === "1"            # => false
Integer === 1              # => true
Float === 1                # => false
Float === 1.0              # => true
Array === []               # => true
Array === {}               # => false
Hash === {}                # => true
Fixnum === Fixnum          # => false
Class === Fixnum           # => true
Class === Class            # => true

Proc

lambda { |x| x % 7 == 0 } === 35     # => true
lambda { |x| x % 7 == 0 } === 36     # => false

User defined class

class MyClass
  def self.===( collection )
    if collection.respond_to?( :length )
      collection.length == 4
    else
      false
    end
  end
end

MyClass === 4             # => false
MyClass === "abc"         # => false
MyClass === "abcd"        # => true
MyClass === [1,2,3]       # => false
MyClass === [1,2,3,4]     # => true
MyClass === :wxyz         # => true
MyClass === { k1: "v1", k2: "v2", k3: "v3" }            # => false
MyClass === { k1: "v1", k2: "v2", k3: "v3", k4: "v4" }  # => true

Advanced Example

class MyClass
  def self.===( collection )
    if collection.respond_to?( :length )
      collection.length == 4
    else
      false
    end
  end
end

def convert( expr )
  numberstrings = [ "first", "second", "third" ]
  
  divisible_by_7 = lambda { |x| Integer === x && x % 7 == 0 }
    
  case expr
  when [], {}, ""
    "This is empty"
  when 10
    "This is number 10"
  when 11
  when (20..30)
    "The number is between 20 and 30"
  when divisible_by_7
    "The number is divisible by 7"
  when Integer
    "This is a number"
  when *numberstrings    # use the splat operator * to expand an array into a list of values
    "This is the first or the second or the third"  
  when MyClass
    "This collection meets the spec"  
  when Array, Hash
    "You should not use an array or a hash"
  when /[Cc]hapter[ ]*(\d*)/
    "This is chapter #{$1}"
  when :special
    "This is something special"
  when String
    "This is a string"  
  when Symbol
    "This is a symbol"
  else
    "Could not match"
  end
end

convert( 10 )               # => "This is number 10"
convert( 11 )               # => nil       : for this when clause no expression defined -> method returns nil
convert( 'mystring' )       # => "This is a string"
convert( :a )               # => "This is a symbol"
convert( 23 )               # => "The number is between 20 and 30"
convert( 35 )               # => "The number is divisible by 7"
convert( 36 )               # => "This is a number"
convert( [1,2,3] )          # => "You should not use an array or a hash"
convert( [1,2,3,4] )        # => "This collection meets the spec"
convert( 'second' )         # => "This is first or second or third"
convert( 'Chapter   56' )   # => "This is chapter 56"
convert( [] )               # => "This is empty"
convert( {} )               # => "This is empty"
convert( "" )               # => "This is empty"
convert( nil )              # => "Could not match"