Ruby Versus Java Conventions

Having spent a long time almost exclusively coding Java since about 2001. I have some mental baggage when it comes to adjusting to Ruby. Excepting some tinkering in PHP and Python in the last five years I’ve primarily been living in an IDE with Java when I actually got to code.

I realize I have this bias, and I’m adjusting, but a small bug fix I submitted to RSpec shows that I still probably have more adjustment to do. The problem isn’t important, and in fact it was just a change to one line of code that was necessary.

The line of code was:

def method_missing(sym, *args, &block)
        if __is_sweetened? sym
          object = self
          calls = sym.to_s.split("_")
          while calls.length > 1
            call = calls.shift
            object = object.__send__(call)
            break if call == "be"
          end
          return object.__send__(calls.join("_"), *args, &block)
        end
        __orig_method_missing(sym, *args, &block)
      end

The important line is

1
break if call == "be"

. The context isn’t critical, but essentially it needed to add conditions to only break if the next element in the array wasn’t the strings ‘a’ or ‘an’. In my head I thought there was probably a nice clean Ruby way to do this, and heck Array has a lot of nice convenience methods, so I started there.

My first attempt came up as:

break if call == "be" && (calls.first != "a" || calls.first != "an")

I found the

1
first

method from Array and I liked that, but the rest of the conditional felt exactly like something I’d write in Java except it would have been:

break if call == "be" && (calls[0] != "a" || calls[0] != "an")

OK, time for a little refactoring for readability. Extract out the conditional to a method and give it a better name.

break if call == "be" && a_or_an_follows_be(calls)
...
def a_or_an_follows_be(calls)
  return (calls.first != "a" || calls.first != "an")
end

At this point I figured it was reasonable even if not probably the most Rubyesque. Luckily the new release of RSpec fixed this bug and I was able to see what they did:

break if call == "be" unless ["an","a"].include? calls[0]

Much more concise, I would have never thought of just using a simple two element array.