TL;DR: Using is_a? is almost always not the best solution.

Let’s say that you own a car dealership and you have standard markup rates for all vehicles previously of 20%. Consider the following models representing this requirement.

class Vehicle < ActiveRecord::Base
  def sale_price
    price * markup_rate
  end

  def markup_rate
    0.2
  end
end

class Car < Vehicle
end

class Suv < Vehicle
end

Your requirements may be stated like this:

If the Vehicle is a SUV we want to charge a markup rate of 40%. The markup rate should remain the same for other vehicles

Solution 1: Using is_a? (or alias kind_of?)

class Vehicle < ActiveRecord::Base
  # Code omitted from base example to focus on the change

  def markup_rate
    self.is_a?(Suv) ? 0.4 : 0.2
  end
end

Here we changed the markup_rate for the vehicle. This may be because Ruby allows us to be expressive. Some downsides to this approach include:

  • Conditional logic is a bit complicated and should be avoided where necessary
  • If we added another Vehicle with another markup_rate, the conditional expression would have several branches
  • It is not entirely obvious which markup_rate is which Vehicle type
  • We pay a performance penalty when we have to do the type checking using is_a?

Is there a better way?

Solution 2: Using pure Object Oriented Programming (OOP)

# Code omitted from base example to focus on the change
class Suv < Vehicle
  def markup_rate
    0.4
  end
end

Here we override the markup_rate method in the child class of the Vehicle.

The second solution is by far better as Ruby is very much an Object Oriented language. A few points why:

  • Simply looking at the Suv’s class implementation and realizing that it has a different markup_rate.
  • The general markup_rate for a Vehicle has not changed, so that class is not touched. This is a big win, as the change should be only applied in the context it is needed.

When to use is_a?

The only time I see this necessary is when our conditions based on classes that we do not own. By that I mean String, Hash, Proc or some class belong to a library we are using.