Delegation in Ruby

This is my first article in http://railsmagazine.com, it was published in issue 1, so basically I'm just republishing it here again.

"Separate changeable parts from others that remain the same" and "composition is preferred to inheritance" are 2 common design principles when you start designing in OOP world. However and while the first seems to be logical, a one might wonder why it's preferable to use composition over inheritance, and that's a logical question, lets answer it via an example:

Let's suppose that we have a Robot that has a heat sensor, a one would write a very simple UML:

This design has several drawbacks:

1.There is a strong probability to have another type of robots that don't have heat sensors(breaks the first design principle: separate changeable code from static one).
2.Whenever I want to modify anything related to the heat sensor, I have to change the robot class(breaks the first design principle).
3.Exposure of heat sensor methods to in Robot class.

Let's enhance this class a bit:

Well, now this is an inheritance based design and it solves the first problem, but it's still incapable to solve the other 2 problems related to the heat sensor. Let's do another enhancement:

Now this is a typical design, based on composition rather than inheritance, where we could solve the above 3 problems, and moreover we gained a new thing: we can now abstract the HeatSensor for future uses.

Now what's delegation?

Delegation is the process of delegating functionality to the contained parts.
If you look carefully at the previous figure, you will notice that the VolcanoRobot is still having the 3 methods that are related to the sensor, well those are a wrapper methods, they do nothing but to call the sensor corresponding ones, and that's exactly what delegation is, just delegate functionality to the contained parts(delegates).
Delegation comes along with composition to provide a flexible neat solutions like the one we had above, and also it serves the principle "separate changeable code from static one" ,but that also comes with a tax: a need of wrapper methods, and extra time needed in processing because of the call of these wrapper methods.

Ruby and delegation

Now let's have a code example:
We have a multi purpose Robot that has an arm and a heat sensor, the robot does several jobs, like packaging boxes, stacking them and measuring the heat.
we will use composition and delegation as follows:

class Robot
  def initialize
    @heat_sensor = HeatSensor.new
    @arm = RobotArm.new
  end

  def measure_heat(scale="c")
    @heat_sensor.measure(scale)
  end

  def stack(boxes_number=1)
    @arm.stack(boxes_number)
  end

  def package
    @arm.package
  end
end

class HeatSensor
  #Celsius or Fahrenheit scale
  def measure(scale="c")
    t = rand(100)
    t = scale=="c" ? t : t * (9/5)
    puts "Heat is #{t}° #{scale.upcase}"
  end
end

class RobotArm
  def stack(boxes_number=1)
    puts "Stacking #{boxes_number} box(es)"
  end

  def package
    puts "Packaging"
  end
end


robo = Robot.new #=>#<Robot:0xb75131e8 @arm=#<robotarm:0xb75131ac>, @heat_sensor=#<heatsensor:0xb75131c0>>
robo.stack 2 #=>Stacking 2 box(es)
robo.package #=>Packaging
robo.measure_heat #=> Heat is 59° C
</heatsensor:0xb75131c0></robotarm:0xb75131ac>

As you can see, i have 3 wrapper methods(stack,package and measure_heat) in Robot class that are doing nothing but to call the contained objects corresponding methods.
This is really a nasty thing, specially when there are lots of contained objects.
However there are 2 libs that comes to the rescue to in ruby, Forwardable and Delegate. Let's check them one by one.

Forwardable lib

Forwardable lib is library that supports delegation, it has 2 modules Forwardable and SingleForwardable:

Forwardable module

The Forwardable module provides delegation of specified methods to a designated object, using the methods def_delegator and def_delegators.

def_delegator(obj, method, alias = method) : Defines a method method which delegates to obj. If alias is provided, it is used as the name for the delegate method.

def_delegators(obj, *methods): Shortcut for defining multiple delegator methods, but with no provision for using a different name.

Let's refactor our robot example to make it Forwardable module:

require 'forwardable'
class Robot
  # Extending provides class methods
  extend Forwardable
  # Use of  def_delegators
  def_delegators :@arm,:package,:stack
  # Use of  def_delegator
  def_delegator :@heat_sensor, :measure ,:measure_heat
  def initialize
    @heat_sensor = HeatSensor.new
    @arm = RobotArm.new
  end
end

class HeatSensor
  #Celsius or Fahrenheit scale
  def measure(scale="c")
    t = rand(100)
    t = scale=="c" ? t : t * (9/5)
    puts "Heat is #{t}° #{scale.upcase}"
  end
end

class RobotArm
  def stack(boxes_number=1)
    puts "Stacking #{boxes_number} box(es)"
  end

  def package
    puts "Packaging"
  end
end

Well, that's a neater solution as you can see.

SingleForwardable module

The SingleForwardable module provides delegation of specified methods to a designated object, using the methods def_delegators. This module is similar to Forwardable, but it works on objects themselves, instead of their defining classes.

require "forwardable"
require "date"
date = Date.today #=> #<Date: 4909665/2,0,2299161>
# Prepare object for delegation
date.extend SingleForwardable #=> #<Date: 4909665/2,0,2299161>
# Add delegation for Time.now
date.def_delegator :Time, "now","with_time"
puts date.with_time #=>Thu Jan 01 23:03:04 +0200 2009

Delegate Lib

Delegate lib is another lib that provides delegation, i'll explain 2 ways to use it:

DelegateClass method

Use the top level DelegateClass method which allows you to easily setup delegation through class inheritance. In the following example, I want to make a new class called CurrentDate, which holds the current date and some extra methods, at the same time I'm delegating to normal date objects:

require "delegate"
require "date"
# Notice the class definition
class CurrentDate < DelegateClass(Date)
  def initialize
    @date = Date.today
    # Pass the object to be delegated to the superclass. 
    super(@date)
  end

  def to_s
    @date.strftime "%Y/%m/%d"
  end

  def with_time
    Time.now
  end
end

cdate = CurrentDate.new
# Notice how delegation works
# Instead of doing cdate.date.day and defining attr_accessor for the date , i'm doing c.day
puts cdate.day #=>1
puts cdate.month #=>1
puts cdate.year #=>2009
# Testing added methods
# to_s
puts cdate #=> 2009/01/01
puts cdate.with_time #=> Thu Jan 01 23:22:20 +0200 2009

SimpleDelegator class

Use it to delegate to an object that might be changed:

require "delegate"
require "date"
today = Date.today #=> #<Date: 4909665/2,0,2299161>
yesterday = today – 1 #=> #<Date: 4909663/2,0,2299161>
date = SimpleDelegator.new(today) #=> #<Date: 4909665/2,0,2299161>
puts date #=>2009-01-01
# Use __setobj__ to change the delegate
date.__setobj__(yesterday)#=> #<Date: 4909663/2,0,2299161>
puts date #=>2008-12-31

As you can see, we made 2 objects and then delegated to them consequently.

What about Rails?

Rails adds new functionality called "delegate":
Which provides a delegate class method to easily expose contained objects’ methods as your own. Pass one or more methods (specified as symbols or strings) and the name of the target object as the final :to option (also a symbol or string). At least one method and the :to option are required.

go to your console and create a dummy project ,then cd to that project, and fire the rails console:

$ rails dummy 
…...
$ cd dummy
$ruby script/console
Loading development environment (Rails 2.2.2)
>> Person = Struct.new(:name, :address)
=> Person
>> class Invoice < Struct.new(:client)
>>   delegate :name, :address, :to => :client
>> end
=> [:name, :address]
>> john_doe = Person.new("John Doe", "Vimmersvej 13")
=> #<struct Person name="John Doe", address="Vimmersvej 13">
>> invoice = Invoice.new(john_doe)
=> #<struct Invoice client=#<struct Person name="John Doe", address="Vimmersvej 13">>
>> invoice.name
=> John Doe
>> invoice.address
=>Vimmersvej 13

I strongly urge you to check the whole provided examples in rails API documetation,to check also how to use this effectively with ActiveRecord.

Before I finish this article I want to share you the code of delegate method form rails API documentation, I'll add some comments on the code to explain you what is going on:

class Module
  # Delegate method 
  # It expects an array of arguments that contains the methods to be delegated 
  # and a hash of options
  def delegate(*methods)
    # Pop up the options hash from arguments array
    options = methods.pop
    # Check the availability of the options hash and more specifically the :to option
    # Raises an error if one of them is not there
    unless options.is_a?(Hash) && to = options[:to]
      raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
    end

    # Make sure the :to option follows syntax rules for method names 
    if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
      raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
    end

    # Set the real prefix value 
    prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_"

   # Here comes the magic of ruby :) 
   # Reflection techniques are used here:
   # module_eval is used to add new methods on the fly which:
   # expose the contained methods' objects
    methods.each do |method|
      module_eval("def #{prefix}#{method}(*args, &block)\n#{to}.__send__(#{method.inspect}, *args, &block)\nend\n", "(__DELEGATION__)", 1)
    end
  end
end

That's it for this article, we have covered 5 points:

1-Composition vs inheritance.
2-What delegation is, and why it's used.
3-Ruby Forwardable lib.
4-Ruby Delegate lib.
5-Rails delegate method.