# <!-- rdoc-file=lib/forwardable.rb -->
# 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`.
#
module Forwardable
  # <!-- rdoc-file=lib/forwardable.rb -->
  # Version of `forwardable.rb`
  #
  VERSION: String

  FORWARDABLE_VERSION: String

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - delegate method => accessor
  #   - delegate [method, method, ...] => accessor
  # -->
  # 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.
  #
  def instance_delegate: (Hash[Symbol | Array[Symbol], Symbol] hash) -> void

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - delegate(hash)
  # -->
  #
  alias delegate instance_delegate

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - 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
  #
  def def_instance_delegators: (Symbol | String accessor, *Symbol methods) -> void

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - def_delegators(accessor, *methods)
  # -->
  #
  alias def_delegators def_instance_delegators

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - 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
  #
  def def_instance_delegator: (Symbol | String accessor, Symbol method, ?Symbol ali) -> void

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - def_delegator(accessor, method, ali = method)
  # -->
  #
  alias def_delegator def_instance_delegator
end

# <!-- rdoc-file=lib/forwardable.rb -->
# SingleForwardable can be used to setup delegation at the object level as well.
#
#     printer = String.new
#     printer.extend SingleForwardable        # prepare object for delegation
#     printer.def_delegator "STDOUT", "puts"  # add delegation for STDOUT.puts()
#     printer.puts "Howdy!"
#
# Also, SingleForwardable can be used to set up delegation for a Class or
# Module.
#
#     class Implementation
#       def self.service
#         puts "serviced!"
#       end
#     end
#
#     module Facade
#       extend SingleForwardable
#       def_delegator :Implementation, :service
#     end
#
#     Facade.service #=> serviced!
#
# If you want to use both Forwardable and SingleForwardable, you can use methods
# def_instance_delegator and def_single_delegator, etc.
#
module SingleForwardable
  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - delegate method => accessor
  #   - delegate [method, method, ...] => accessor
  # -->
  # Takes a hash as its argument.  The key is a symbol or an array of symbols.
  # These symbols correspond to method names.  The value is the accessor to which
  # the methods will be delegated.
  #
  def single_delegate: (Hash[Symbol | Array[Symbol], Symbol] hash) -> void

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - delegate(hash)
  # -->
  #
  alias delegate single_delegate

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - def_single_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
  #
  def def_single_delegators: (Symbol | String accessor, *Symbol methods) -> void

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - def_delegators(accessor, *methods)
  # -->
  #
  alias def_delegators def_single_delegators

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - def_single_delegator(accessor, method, new_name=method)
  # -->
  # Defines a method *method* which delegates to *accessor* (i.e. it calls the
  # method of the same name in *accessor*).  If *new_name* is provided, it is used
  # as the name for the delegate method. Returns the name of the method defined.
  #
  def def_single_delegator: (Symbol | String accessor, Symbol method, ?Symbol ali) -> void

  # <!--
  #   rdoc-file=lib/forwardable.rb
  #   - def_delegator(accessor, method, ali = method)
  # -->
  #
  alias def_delegator def_single_delegator
end
