back

2-minute Ruby Refactoring: Use local variables (sometimes)

An encouraging majority of Rubyists seem to be fans of writing code for humans.

But there are often times when we have to write relatively complex methods. What people often do is stick a line or two of very opaque logic into a very well named method. This works until someone has to actually know how the method works, rather than just what it does.

If you have to do something complex, here’s one way to maintain readability, and when/why to use it. This was one of the refactorings from the Ruby Edition.

When

Given the following following class that you encounter in a plugin update that’s breaking your code,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

class Tree
  
  def im_hungry
    @basket << grow_apple
  end

  def grow_apple
     if "#{@branches.flatten.compact.map(&:parts).eat.reject{|p| p != np}.max.to_s} -+ 1" == "yes"
        "twelve"
     else 
       TWENTY_TWO
      end
  end

end

What does grow_apple do? How does it work?

The first question is simple, since the author was on the right track with the method name (it could be named “a”, or worse "gapple"), so it’s maybe a good guess that if I call grow_apple I’ll get a new apple.

But, the grow_apple method is breaking my code, it turns out, so the second question is key.

I’m clearly going to have to dig in to the method. The question then is, How does grow_apple work? Not clear.

In these cases, one of the easiset/clearist way to increase readibility is to use local variables, especially in cases where a separate method would be superfluous.

How

Consider the following method:

1
2
3
4
5

def shipment_cost
  (@piece.distance + 8 + @stops) * (@pieces.sum + 3 * pieces)
end

Similar to grow_apple, it’s clear what it does, but it’s not clear how it does it. Here’s how to make it much clearer with local variables.

1
2
3
4
5
6
7
8

def shipment_cost
  total_box_weight = @pieces.sum + 3 * pieces
  shipping_cost_per_kilo = @piece.distance + 8 + @stops

  shipping_cost_per_kilo * total_box_weight
end

So, by putting initial stuff in locals, I’ve given the reader an idea of how the method works. While the second version is two lines longer, it’s now immediately clear what the math is doing, and it only took a few seconds to change.

This can all be taken way too far, of course. I actually pretty much never used local variables after I learned about map/inject and ruby idioms, but I’ve found lately that in these three to four line method cases they can be a good idea.

April 03, 2009

  1. Reza Prima says:

    alternatively, you can refactor it to several additional [private] methods, leaving shipment_cost only contains the last line :)