module Forwardable

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

For example, say you have a class RecordCollection which contains an array @records. You could provide the lookup method record_number(), which simply calls [] on the @records array, like this:

require 'forwardable'

class RecordCollection
  attr_accessor :records
  extend Forwardable
  def_delegator :@records, :[], :record_number
end

We can use the lookup method like so:

r = RecordCollection.new
r.records = [4,5,6]
r.record_number(0)  # => 4

Further, if you wish to provide the methods size, <<, and map, all of which delegate to @records, this is how you can do it:

class RecordCollection # re-open RecordCollection class
  def_delegators :@records, :size, :<<, :map
end

r = RecordCollection.new
r.records = [1,2,3]
r.record_number(0)   # => 1
r.size               # => 3
r << 4               # => [1, 2, 3, 4]
r.map { |x| x * 2 }  # => [2, 4, 6, 8]

You can even extend regular objects with Forwardable.

my_hash = Hash.new
my_hash.extend Forwardable              # prepare object for delegation
my_hash.def_delegator "STDOUT", "puts"  # add delegation for STDOUT.puts()
my_hash.puts "Howdy!"

Another example

You could use Forwardable as an alternative to inheritance, when you don’t want to inherit all methods from the superclass. For instance, here is how you might add a range of Array instance methods to a new class Queue:

class Queue
  extend Forwardable

  def initialize
    @q = [ ]    # prepare delegate object
  end

  # setup preferred interface, enq() and deq()...
  def_delegator :@q, :push, :enq
  def_delegator :@q, :shift, :deq

  # support some general Array methods that fit Queues well
  def_delegators :@q, :clear, :first, :push, :shift, :size
end

q = Thread::Queue.new
q.enq 1, 2, 3, 4, 5
q.push 6

q.shift    # => 1
while q.size > 0
  puts q.deq
end

q.enq "Ruby", "Perl", "Python"
puts q.first
q.clear
puts q.first

This should output:

2
3
4
5
6
Ruby
nil

Notes

Be advised, RDoc will not detect delegated methods.

forwardable.rb provides single-method delegation via the def_delegator and def_delegators methods. For full-class delegation via DelegateClass, see delegate.rb.

Constants

FORWARDABLE_VERSION
VERSION

Version of forwardable.rb

Attributes

debug [RW]

ignored

Public Instance Methods

def_delegator (accessor, method, ali = method)
def_delegators (accessor, *methods)
def_instance_delegator (accessor, method, ali = method)

Define method as delegator instance method with an optional alias name ali. Method calls to ali will be delegated to accessor.method. accessor should be a method name, instance variable name, or constant name. Use the full path to the constant if providing the constant name. Returns the name of the method defined.

class MyQueue
  CONST = 1
  extend Forwardable
  attr_reader :queue
  def initialize
    @queue = []
  end

  def_delegator :@queue, :push, :mypush
  def_delegator 'MyQueue::CONST', :to_i
end

q = MyQueue.new
q.mypush 42
q.queue    #=> [42]
q.push 23  #=> NoMethodError
q.to_i     #=> 1
# File lib/forwardable.rb, line 188
def def_instance_delegator(accessor, method, ali = method)
  gen = Forwardable._delegator_method(self, accessor, method, ali)

  # If it's not a class or module, it's an instance
  mod = Module === self ? self : singleton_class
  ret = mod.module_eval(&gen)
  mod.__send__(:ruby2_keywords, ali) if RUBY_VERSION >= '2.7'
  ret
end
Also aliased as: def_delegator
def_instance_delegators (accessor, *methods)

Shortcut for defining multiple delegator methods, but with no provision for using a different name. The following two code samples have the same effect:

def_delegators :@records, :size, :<<, :map

def_delegator :@records, :size
def_delegator :@records, :<<
def_delegator :@records, :map
# File lib/forwardable.rb, line 156
def def_instance_delegators(accessor, *methods)
  methods.each do |method|
    next if /\A__(?:send|id)__\z/ =~ method
    def_instance_delegator(accessor, method)
  end
end
Also aliased as: def_delegators
delegate method → accessor
delegate [method, method, ...] → accessor
Alias for: instance_delegate
instance_delegate
Also aliased as: delegate