Refactoring If Statements Out of Your Ruby Code

April 25, 2017

Refactoring If Statements Out of Your Ruby Code

As I kept writing more code, I found I didn’t like conditionals all that much, especially when they started to obscure the purpose of the code. A technique I found from this blog post is described below.

Ruby basics

Why would you want to refactor?

I wanted to refactor to clean things up and make it easier to add new features going forward. Too much conditional logic (and nested logic) forces me to spend more time reasoning about code than I want to.

When would you want to refactor?

It’s really a personal choice. In a startup environment where things move fast, I tend to refactor in small bits in order to make it just a bit easier for someone else going forward.

I also tend to feel more comfortable refactoring when there is a robust test suite surrounding the code. This way you’ll know if your refactoring breaks something.

Example: Reasons for Unsubscribing from an Email List Code

Here’s a somewhat contrived example of conditional code loosely based on something I saw in production:

@reason_recorder = []
['too many emails', 'no longer interested', 'other reason'].each do |reason|
  if reason == 'too many emails'
    @reason_recorder << "too many emails from you"
  if reason == 'no longer interested'
    @reason_recorder << "no longer interested in this stuff"
  if reason == 'other reason'
    @reason_recorder << "another reason"

Step 1: Build a collection of reasons

We build a collection of UnsubscribeReason classes housed by an UnsubscriberReasons class.

class UnsubscribeReasons
  def self.all
    [TooManyEmails, NoLongerInterested, OtherReason]

Step 2: Build a reason class for the reason classes in Step 1 to inherit from

We build the parent class for the collection of classes in Step 1.

class UnsubscribeReason
  attr_accessor :reason_props

  def initialize(reason_props)
    @reason_properties = reason_props

Step 3: Build the actual UnsubscribeReason classes

Now we build the actual UnsubscribeReason classes. You’ll notice they have an introspection property hasreason?_ that expects an OpenStruct with a reasontypes_ property so we can check whether a specific “unsubscribe reason” such as too many emails exists.

class TooManyEmails < UnsubscribeReason
  def self.has_reason?(reason_props)

class NoLongerInterested < UnsubscribeReason
  def self.has_reason?(reason_props)

class OtherReason < UnsubscribeReason
  def self.has_reason?(reason_props)

Step 4: Build a factory to fetch the reasons

Next we build a collection of UnsubscribeReasons that includes the classes that map to the various reasons a user unsubscribed.

class UnsubscribeReasonFactory
  def self.build_collection(reason_props) { |reason| reason.has_reason?(reason_props) }.map { |reason| }

Step 5: Build a reasoner class to house the reasons

Finally, we build an UnsubscribeReasoner class with a reason list to house all the valid unsubscribe reason we found.

class UnsubscribeReasoner
  attr_accessor :reason_list

# use reason_list as the pivot point in the code that contains if statements
  def initialize
    @reason_list = []

  def update_reason(reason_type:)
    reason_type =
  rescue NoMethodError

  def update_reason_with_too_many_emails
    reason_list << "I received too many emails"

  def update_reason_with_no_longer_interested
    reason_list << "I am no longer interested"

  def update_reason_with_other_reason
    reason_list << "I have another reason"

Step 6: Refactor the conditional code to use our new classes

reason_props = "", reason_types: [:too_many_emails, :no_longer_interested, :other_reason])
unsubscribe_reasons = UnsubscribeReasonFactory.build_collection(reason_props)
unsubscribe_reasoner =
unsubscribe_reasons.each do |reason|
  unsubscribe_reasoner.update_reason(reason_type: reason.class)

p unsubscribe_reasoner.reason_list

# => ["I received too many emails", "I am no longer interested", "I have another reason"]

Maybe it’s a bit too contrived

The nice thing about the above refactoring is that if we want to add another unsubscribe reason, we more or less just have to add a class. The code is a bit longer, but I’ve been using contrived example code so saying the code is longer is a bit of an unfair statement.

You’ll have to try and see what works for you.


Overall, this is an attempt to show how you can refactor conditional logic out of your Ruby code. The example repository containing all this code is here.