Cleaning Up Conditionals with Send

Published on March 6, 2017

Well well well, here we are. In the words of a song my friend wrote for our band in high school, "It's been so long, but now we're back." Lyrical genius, I say. Anyways, it's been quite a while since I posted, but I thought I might try and resurrect this blog a little with some slightly more technical content. I've been programming pretty much every day for over a year now, and I feel like I've done some kinda cool things. Lets dive in!

So I wanna talk a bit about something cool I learned at work a few months back. I'd been working with large, somewhat irregular JSON files, and had to parse certain values out of the data. The fields would come in different forms, sometimes a string, sometimes an array, sometimes a hash. My first attempt at solving the issue looked something like this:

if input.is_a?(String)
  call_string_method(input)
elsif input.is_a?(Array)
  call_array_method(input)
elsif input.is_a?(Hash)
  call_hash_method(input)
end

It gets the job done, and to my novice programming brain, it was the proper way to do it. But what happens when you decide in the future that you need to handle a Float differently from an Integer? Or that you want to add custom objects to the tree? You can add as many elsif lines to an if statement as you want, but it's ugly and hard to reason about. My boss showed me this cool way of handling this kind of issue, which has been a standard in my toolbox ever since that day.

First, we define the handler methods we'd need anyways.

def handle_string(input)
  # do stuff
end

def handle_array(input)
  # do stuff
end

def handle_hash(input)
  # do stuff
end

Then, we find the class of the input, and using a clever combination of the Object#send method and string interpolation, we call the method dynamically, like this.

input_klass = input.class.to_s.downcase
send("handle_#{input_klass}", input)

Isn't that much cleaner? We get the string value of the class of input, and call the send method with a string with some interpolation as the first argument, and our input object as the second argument.

send is a cool little method, defined on the Object class, which is the parent of just about every Ruby class. Basically, it identified a method by it's name as a string, and calls it with whatever arguments are passed along. This allows you to call methods using string interpolation to call one or the other, rather than using a complicated set of if-else blocks to make that decision. The documentation is here if you want to read more.

That's about all I got for now, but thanks for reading, and I hope someone out there finds this useful!