module Forwardable - forwardable: Ruby Standard Library Documentation

# 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)
Alias for: def_instance_delegator
def_delegators(accessor, *methods)
Alias for: def_instance_delegators
def_instance_delegator(accessor, method, ali = method) click to toggle source

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 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) click to toggle source

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 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 [method, method, ...] => accessor click to toggle source

Takes a hash as its argument. The key is a symbol or an array of symbols. These symbols correspond to method names, instance variable names, or constant names (see `def_delegator`). The value is the accessor to which the methods will be delegated.

    # File forwardable.rb, line 135
    def instance_delegate(hash)
      hash.each do |methods, accessor|
        unless defined?(methods.each)
          def_instance_delegator(accessor, methods)
        else
          methods.each {|method| def_instance_delegator(accessor, method)}
        end
      end
    end

Also aliased as: delegate