Tuesday, December 2, 2008

A 'Ruby best practice' - return values

Over on the wonderful Ruby blog 'On Ruby' they are holding a contest requesting that one post a best practice from another language or community and how it translates into Ruby. It just so happens there is one that I have personally applied to all my Ruby programming even though it is not a Ruby convention!

Return values in Ruby, where are they?
It really bothers me that code can become unclear or obscure by not communicating very simple facts, like what a method is returning. A good practice used in Java is to return a 'results', meaning one keeps the return value of a method stored in a variable named 'results'. It can be a String, Boolean, or whatever.

Ruby is one of my favorite languages right now, but handles return values for methods very differently than languages like Java. Ruby does not require an explicit 'return' statement, but can return the last executed statement results by default. This can be confusing.

Discovering what this return value might be can be more time consuming that is necessary and is immediately taken care of by simply supplying a 'return' statement. Cost is nothing, results are clarity. I provide an example from my current running project, AbTLinux:

##
# Cleans up this packages source build directory.
#
# RETURNS:  boolean - True if the completes successfully,
# otherwise false.
##
def remove_build
  puts "Removings build..."
  if ($REMOVE_BUILD_SOURCES)
      buildSourcesLocation = "#{$BUILD_LOCATION}/#{srcDir}"
       
      if (!File.directory?(buildSourcesLocation))
        return true
      end

      if (!FileUtils.rm_rf buildSourcesLocation, :verbose => true )
        return false
      end
  end
   
  return true
end

4 comments:

  1. Hi Eric,
    thanks for the kinds words about On Ruby, and for the contest submission. Good luck!

    ReplyDelete
  2. Hi Pate,

    Any time, keep putting up the interesting Ruby content. There are not as many good sites that are active on this front as one would imagine.

    ReplyDelete
  3. I suppose it's partly a matter of taste, but I have to disagree with you. Explicit returns are too much like gotos and make it harder to follow the control flow. I much prefer a functional style, which is quite natural in Ruby.

    Also, this code reads like Java code, not Ruby code. And I find it ironic that the code sample you use has so many examples of poor Ruby practices and non-Ruby style. Camel-case variables, comments with typos and missing words, unnecessary parens for conditionals, missing parens for method calls, using string interpolation instead of File.join to construct a path, and using global variables. I don't know if this is your own code or someone else's, but it's not a great choice to show good style or practices.

    I'd write the method as so:

    def remove_build
    puts "Removing build..."
    if $REMOVE_BUILD_SOURCES
    sources_path = File.join($BUILD_LOCATION, src_dir)
    File.directory?(sources_path) && FileUtils.rm_rf(sources_path, :verbose => true)
    else
    true
    end
    end

    ReplyDelete
  4. Josh,

    Thanks for the input, constructive comments always welcome!

    You are right on most of this, I have chosen a piece of code I wrote in the very beginning of my Ruby experience. Most of this project has been refactored, but this one has not yet made it. Furthermore, my day job is indeed Java, so the transition to Ruby indeed looked a bit Java-ish. The point was only to illustrate the explicit return statements.

    The camel-cased variables should be removed, the parens are a style choice (I like more than less for clarity, but his is another best-practice story), and global variables are part of the design choice. The File.join tip is a good one and I will be applying that one for sure.

    I understand that a functional programmer would prefer less (as your method shows). It is indeed a matter of choice/style.

    ReplyDelete