Forwardable Module

·

3 min read

Forwardable module is able to give new behaviour to an object by delegating the method to it, using def_delegator or def_delegators.

Let’s say we have this

class HousingUnit
  attr_reader :example

  def initialize
    @example = [:apartment, :flat, :residence]
  end
end

newhouse = HousingUnit.new
puts "This year I'm gonna get myself new #{newhouse.example.sample}!"

This will return This year I'm gonna get myself new apartment! as sample method will return random element from the array. However, it will be more readable if we have a method that can return the random element directly.

class HousingUnit
  attr_reader :example

  def initialize
    @example = [:apartment, :flat, :residence]
  end

  def type
    @example.sample
  end
end

newhouse = HousingUnit.new
puts "This year I'm gonna get myself new #{newhouse.type}!"

This will give us This year I'm gonna get myself new residence!. Perfect! The HousingUnit#type has power to access the housing unit example type.

Fast forward, Ruby provides a way to perform this kind of action by using def_delegator. To use it, we have to use keyword extend to add module method at class level. Then, we need to define the delegator method. The formula is def_delegator :an_instance, :the_delegated_method, :the_alias_method like this one

require 'forwardable'

class HousingUnit
  extend Forwardable
  attr_reader :example
  def_delegator :@example, :sample, :type

  def initialize
    @example = [:apartment, :flat, :residence]
  end

  # def type
  #   @example.sample
  # end
end

newhouse = HousingUnit.new
puts "This year I'm gonna get myself new #{newhouse.type}!"

This will give us This year I'm gonna get myself new residence!. Note that I’m calling forwardable library here to be able to use the method.

How about def_delegators?

In whole view it works similar like def_delegator. The difference is def_delegators able to handle multiple methods but those methods can’t have alias name of method.

require 'forwardable'

class HousingUnit
  extend Forwardable
  attr_reader :example
  def_delegators :@example, :sample, :size

  def initialize
    @example = [:apartment, :flat, :residence]
  end
end

newhouse = HousingUnit.new
puts "Total number of housing unit types: #{newhouse.size} "
puts "Random example of housing unit: #{newhouse.sample}"

This will produce

Total number of housing unit types: 3 
Random example of housing unit: apartment