Method Delegation In Ruby
When creating a new Ruby class, it can be helpful to use composition over
inheritance. Let’s look at an example of an Image. Our Image has a
File, but we don’t want the Image to extend File. Why not? We don’t want
Image to have the full interface of File; just a small subset of it. This is
a perfect use case for composition and method delegation.
Here’s what our Image class looks like right now. It contains the File
object we’re interested in.
class Image
  def initialize(image_path)
    @file = File.open(image_path)
  end
end
Next, we would like to expose a few of the File methods on Image: #path,
#read, and #close. One way to do that is by writing our own methods that
pass the message along.
require 'forwardable'
class Image
  def initialize(image_path)
    @file = File.open(image_path)
  end
  def path
    @file.path
  end
  def read
    @file.read
  end
  def close
    @file.close
  end
end
This quickly gets repetitive and verbose.
Fortunately, Ruby provides an easy means of delegating methods to other objects.
We’ll extend the Forwardable module, and then use def_delegator to hook up
the delegation.
class Image
  extend Forwardable
  def initialize(image_path)
    @file = File.open(image_path)
  end
  def_delegators :@file, :path, :read, :close
end
We’ve accomplished the same functionality in fewer lines, and made it more readable.
Unfortunately, I find Ruby’s core method delegation unintuitively named and
implemented. It’s confusing to remember the Forwardable module gives us method
delegation, and that the method is named def_delegators. I wrote this small
post so I can refer to it later, when I need a refresher on how to do method
delegation in Ruby.