Susan Potter
snippets: Created / Updated

Ruby Idioms, Part 4: The splat

To splat or not to splat, that is the question.

In Java to unpack an array's values into separate variables you would need to do something like the following:

    burgers = [:hamburger, :bocaburger, :gardenburger, :turkeyburger]
    t = burgers[0]
    u = burgers[1]
    v = burgers[2]
    w = burgers[3]

In the Ruby mindset this would look more like the following:

    burgers = [:hamburger, :bocaburger, :gardenburger, :turkeyburger]
    t, u, v = *burgers # t=>:hamburger, u=> :bocaburger, v=>:gardenburger
    t, u, v, w = *burgers # t=>:hamburger, u=>:bocaburger, v=>:gardenburger, w=>:turkeyburger
    t, u, v, w, x = *burgers # t=>:hamburger, u=>:bocaburger, v=>:gardenburger, w=>:turkeyburger, x=>nil

Think of * (or splat) as the Ruby way of unpacking the elements of an Array into separate variables in Ruby. This brings me to an even nicer example. Many moons ago, when I was working on a Java/J2EE project, I found a piece of code that looked something like the following (except it has been Rubyfied for this blog entry so that I do not get sued for sharing outrageously fantastic proprietary code with the public - ok, ok….even lawyers would understand this couldn't be called outrageously fantastic even in a parallel universe):

    class RunnableWithParams << Thread
      def initialize(param1)
        @param1 = param1
      end

      def initialize(param1, param2)
        @param1 = param1
        @param2 = param2
      end

      def initialize(param1, param2, param3)
        @param1 = param1
        @param2 = param2
        @param3 = param3
      end

      def run
        # some code the uses param1, param2, etc. - in Java this was an empty method body or abstract  - can't remember
      end
    end

Rubifying the the code sure does take some of the sting out of it, but you can probably still feel my initial pain when I realized what kind of code I was inheriting.

The class name and the variables are in fact the same as the original code. The problem this tried to solve, which I suppose it did solve in a stupid way, was to allow Java code to pass parameters over to the thread's logic (which, at least, at the time was not possible to do) in the context of an anonymous class implementation of the method. Now if you wanted to pass in 4 parameters, you were out of luck OR you had to beg the core framework people to add that constructor OR you had to do an ugly JAR patch hack and put your own implementation of RunnableWithParams class ahead of the core framework JAR in your CLASSPATH. They were all pretty ugly scenarios. Of course, you could pass in a List or Map instance, but that really isn't the point.

The most obvious optimization to the code from a readability, extensibility and utility perspective would be to pass in a list to the class's constructor and use elements in the list in the run method logic. However, Ruby provides a nicer way of doing things from the perspective of the client code:

%pre :preserve

class RunnableWithParams << Thread
  def initialize(*params)
    @params = params
  end
  def run # needs to be defined by clients
  end
end

The client code for using this class would look like the following:

    rwp = RunnableWithParams.new param1, param2, param3, param4
    # instead of the initially optimized suggestion, where the client code would look like the following
    rwp = RunnableWithParams.new [param1, param2, param3, param4]

In my view the first line of the snippet above is slightly superior because it is more natural in the way it reads. Again, different people will have different views on this perhaps, but both lines of code provide a decent solution.

If you enjoyed this content, please consider sharing this link with a friend, following my GitHub or LinkedIn accounts, or subscribing to my RSS feed.