BinaryWebPark

How the Send Method Can Help You DRY Your Ruby Code

August 15, 2012

If you’ve hung around the Ruby on Rails community for any length of time, you may have heard about the acronym DRY. DRY stands for “Don’t Repeat Yourself.” In a nutshell, it means programmers should strive to reduce repetitive information.

Let’s look at a non-DRY example

In a C++ program, you typically declare a function twice, once in the header file and once in the main code file. So for instance, your header file subtract.h might look like this:

#ifndef SUBTRACT_H
#define SUBTRACT_H
int subtract(int x, int y); // function prototype for subtract.h
#endif

And your source file subtract.cpp might look like this:

int subtract(int x, int y) {
  return x-y;
}

Notice the repetition of declaring the function “subtract” with its arguments in both places. That means if you ever wanted to change the name of the function or the number of arguments, you would have to make a change in 2 places. This is not such a big deal in our example, but if you multiply this effect in a large codebase with hundreds (or thousands) of functions, you can imagine how non-DRY code would be harder to maintain than DRY code.

Now let’s look at an example in Ruby that illustrates the DRY principle

Suppose we have a Car class defined in our Ruby program.

class Car
  attr_accessor :wheels, :type, :num_passengers
  def initialize(wheels=4, type="hybrid", num_passengers=4)
    @wheels = wheels
    @type = type
    @num_passengers = num_passengers
  end
end

And we write some code to gather information about an instance of our car class:

@new_car = Car.new(4,"conventional", 5)
puts "What do you want to know?"
info = gets.chomp</code>

if info=="wheels"
  puts @new_car.wheels
elsif info=="type"
  puts @new_car.type
elsif info=="num_passengers"
  puts @new_car.num_passengers
else
  puts "Information unavailable"
end

Now the code does the job, but it can be DRY-er using the send method

Let’s change the “if…end” block of code into:

if @new_car.respond_to?(info)
  puts @new_car.send(info)
else
  puts "Information unavailable"
end

So what’s going on with respond_to?

The respond_to? method tests a ruby object’s ability to respond to messages. It allows us to determine ahead of time whether the ruby object knows how to handle the message we want to send it. So in the example above, if @new_car doesn’t respond to the method we want to call, we output a message saying “Information unavailable.”

Notice we went from 9 lines of code down to 5, a 44% reduction in lines of code

Not only that, but if we ever changed the method names in our class (defined automatically by the attr_accessor), say from wheels to numwheels, we wouldn’t have to change the 5 line “if..end” code block with the `respondto?` method. But we would have to change the previous 9 lines of “if..end” code. So we also get a boost in ease of maintainability of our example code base.

So look for ways to use send and respond_to? in your Ruby code. It’ll help keep things DRY.