1. Technology
You can opt-out at any time. Please refer to our privacy policy for contact information.

Ruby Tricks: Expressive Case Statements

Abusing the Case Equality Operator

By

Case statements in Ruby are already kind of a big shortcut to complicated if/elsif statements and there are a lot of tricks you can already do without stepping outside the normal bounds of Ruby idioms. However, there's always something more you can do. In this article, we'll extend the case statement with a few Procs.

How Case Works

The case statement is a bit different in Ruby. The case statement is obviously inspired by the C switch statement (which also uses the "case" keyword in its syntax), but works in a very different way. The C version is a shorthand for ==, give it a value and it will start at the list and compare each of the case statement's values with the value given. Ruby actually works similarly, though gives it a very dynamic twist, it uses the === operator.

The == operator should be used for equality only. Not necessarily testing if two objects are the same object, but testing if they're equal. This could mean testing if all the instance variables are equal, or if they produce the same result on some operation. What this means is up to the class of the object, but it should test for equality. The === operator is a bit different, and is open to interpretation. The === operator is often referred to as the case equality operator, and its primary use is in the when clauses of case statements. This operator's primary job is to make case statements readable. For example, the Class#=== method tests if its argument is of the same class, allowing you to do things like this.


case some_unknown_object
when String
  puts "It's a string!"
when Numeric
  puts "It's a number!"
end

 

Enter Proc

Now enter Proc. A Proc object holds some callable code. Think of it like a method that's not bound to any method name, it can be passed around. We can use this to provide small snippets of code that make case statements easier to read in certain situations. The Proc#=== method calls the proc with any parameters given to it, all that's left to do is for you to return true or false from this proc and you can begin using it in your case statements.

In this example, we'll have a Balance class. This Balance class holds the balance of an account, and we'll come up with a few descriptive Proc objects that can classify that balance. At first this might seem a bit silly with such a simple example, but I'll explain below.


class Balance
  attr_accessor :balance
  
  IN_THE_RED = Proc.new{|b| b.balance < 0 }
  IN_THE_BLACK = Proc.new{|b| b.balance >= 0 }
  
  def initialize(balance=0.0)
    @balance = balance
  end
end

balances = [ Balance.new(-1.0), Balance.new(1.0) ]
balances.each do|b|
  case b
  when Balance::IN_THE_RED
    puts "Balance is in the red: #{b.balance}"
  when Balance::IN_THE_BLACK
    puts "Balance is in the black: #{b.balance}"
  end
end

 

By defining a few constants that store Proc to little descriptive queries about the state of a balance, you can very easily write a case statement without breaking DRY principals as other code can also use these procs, using an english-like syntax that's readable, and without breaking encapsulation as the case statement doesn't actually need to know what "in the red" and "in the black" mean. It doesn't need to peer into the state of the balance, that's kept within a definition in the balance code.

Should You Use It

This should really be the first thing you should ask yourself when encountering any such "trick." And with any trick, it's a mixed bag. It's called a "trick" because it's not immediately obvious you should be able to do this. It may also surprise (and not necessarily in a good way) anyone reading the code. While the reader may know what the === case equality operator is, they may not know what IN_THE_RED is, that it is a Proc object, or what === on a Proc object does. In an attempt to make the code more clear, it is now less clear. For such simplistic examples, it may make more sense to stick to if/elsif statements and implement simpler in_the_red? style query methods.

  1. About.com
  2. Technology
  3. Ruby
  4. Advanced Ruby
  5. Ruby Tricks: Expressive Case Statements

©2014 About.com. All rights reserved.