In Progress
Unit 1, Lesson 1
In Progress

Naked Splat

Video transcript & code

In the last episode we looked at how to ignore individual block arguments we don't care about, using the special _ variable name.

Today, let's look at another example of ignoring arguments. Let's say we're writing a class that builds on top of Ruby's standard Net::HTTP class. The idea is that it will add a caching layer on top of the built-in HTTP functionality. To do so, we want to instantiate a @cache object in the initializer, and stash the results in it whenever a request is made.

require 'net/http'

class CachedHTTP < Net::HTTP
  def initialize
    @cache = {}
    super
  end

  def request
    super
    # ...store result in the cache...
  end
end

Of course, we need to make it possible for clients of this class to pass in various connection parameters to the base class initializer. We could go look up the arguments to the original class, duplicate them, and then pass them into the call to super. That means we also have to duplicate any default arguments, if we want our class to behave just like the base class.

require 'net/http'

class CachedHTTP < Net::HTTP
  def initialize(
      address, 
      port   = nil, 
      p_addr = :ENV, 
      p_port = nil, 
      p_user = nil, 
      p_pass = nil)
    @cache = {}
    super(address, port, p_addr, p_port, p_user, p_pass)
  end

  def request
    super
    # ...store result in the cache...
  end
end

Not only is this a lot of work, it will break if the Net::HTTP interface ever changes. This isn't a major worry for a standard library, but when we're building on in-house code or third-party gems it can be a very real concern.

We might remember that super with no parentheses after it will implicitly pass along all of the arguments. That at least gets rid of some of the mess, but we still have a very long list of arguments that we never actually use.

require 'net/http'

class CachedHTTP < Net::HTTP
  def initialize(
      address, 
      port   = nil, 
      p_addr = :ENV, 
      p_port = nil, 
      p_user = nil, 
      p_pass = nil)
    @cache = {}
    super
  end

  def request
    super
    # ...store result in the cache...
  end
end

At this point we might recall the splat operator. We could use it to slurp all of the arguments into a single array.

require 'net/http'

class CachedHTTP < Net::HTTP
  def initialize(*args)
    @cache = {}
    super
  end

  def request
    super
    # ...store result in the cache...
  end
end

This is considerably nicer. But it's still a little bothersome that we have a variable named args that we never actually use. Fortunately, Ruby has anticipated this situation. If we are never going to use the variable that the splat is applied to, we can dispense with the variable name altogether, leaving just the splat. Paul Battley calls this a "naked splat", which I think is apt.

require 'net/http'

class CachedHTTP < Net::HTTP
  def initialize(*)
    @cache = {}
    super
  end

  def request
    super
    # ...store result in the cache...
  end
end

In effect, this says "I don't care what arguments this method receives". We can go ahead and apply that to our #request method as well. In this case, we care about the first argument, which will be a Request object containing, among other things, the URL of the resource to be requested. But we don't care about any other arguments that come after it. Rather than look up what other arguments the base #request method takes in order to pass them through, we can just apply the naked splat and be done with it.

require 'net/http'

class CachedHTTP < Net::HTTP
  def initialize(*)
    @cache = {}
    super
  end

  def request(request, *)
    super
    # ...store result in the cache...
  end
end

Use of the naked splat is a good habit to get into anytime we override base class methods with the intent of passing the given arguments through unchanged. Rather than trying to match a base interface that may change out from under us after we've gone to the trouble to copy it, we can just apply the naked splat to arguments we don't care about.

That's all for now. Happy hacking!

Responses