Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 266 lines (253 sloc) 7.282 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
#
# forwardable.rb -
# $Release Version: 1.1$
# $Revision: 25189 $
# by Keiju ISHITSUKA(keiju@ishitsuka.com)
# original definition by delegator.rb
# Revised by Daniel J. Berger with suggestions from Florian Gross.
#
# Documentation by James Edward Gray II and Gavin Sinclair
#
# == Introduction
#
# This library allows you delegate method calls to an object, on a method by
# method basis.
#
# == Notes
#
# Be advised, RDoc will not detect delegated methods.
#
# <b>forwardable.rb provides single-method delegation via the
# def_delegator() and def_delegators() methods. For full-class
# delegation via DelegateClass(), see delegate.rb.</b>
#
# == Examples
#
# === Forwardable
#
# Forwardable makes building a new class based on existing work, with a proper
# interface, almost trivial. We want to rely on what has come before obviously,
# but with delegation we can take just the methods we need and even rename them
# as appropriate. In many cases this is preferable to inheritance, which gives
# us the entire old interface, even if much of it isn't needed.
#
# 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 = 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
#
# <i>Prints:</i>
#
# 2
# 3
# 4
# 5
# 6
# Ruby
# nil
#
# 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 use to Class or Module.
#
# module Facade
# extend SingleForwardable
# def_delegator :Implementation, :service
#
# class Implementation
# def service...
# end
# end
#
# If you want to use both Forwardable and SingleForwardable, you can
# use methods def_instance_delegator and def_single_delegator, etc.
#
# If the object isn't a Module and Class, You can too extend
# Forwardable module.
# printer = String.new
# printer.extend Forwardable # prepare object for delegation
# printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts()
# printer.puts "Howdy!"
#
# <i>Prints:</i>
#
# Howdy!

#
# 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 <tt>@records</tt>. You could provide the lookup method
# #record_number(), which simply calls #[] on the <tt>@records</tt>
# array, like this:
#
# class RecordCollection
# extend Forwardable
# def_delegator :@records, :[], :record_number
# end
#
# 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
# # extend Forwardable, but we did that above
# def_delegators :@records, :size, :<<, :map
# end
# f = Foo.new
# f.printf ...
# f.gets
# f.content_at(1)
#
# Also see the example at forwardable.rb.

module Forwardable
  FORWARDABLE_VERSION = "1.1.0"

  @debug = nil
  class<<self
    attr_accessor :debug
  end

  # 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.
  #
  # :call-seq:
  # delegate method => accessor
  # delegate [method, method, ...] => accessor
  #
  def instance_delegate(hash)
    hash.each{ |methods, accessor|
      methods = methods.to_s unless methods.respond_to?(:each)
      methods.each{ |method|
        def_instance_delegator(accessor, method)
      }
    }
  end

  #
  # 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(accessor, *methods)
    methods.delete("__send__")
    methods.delete("__id__")
    for method in methods
      def_instance_delegator(accessor, method)
    end
  end

  def def_instance_delegator(accessor, method, ali = method)
    line_no = __LINE__; str = %{
def #{ali}(*args, &block)
begin
#{accessor}.__send__(:#{method}, *args, &block)
rescue Exception
$@.delete_if{|s| %r"#{Regexp.quote(__FILE__)}"o =~ s} unless Forwardable::debug
::Kernel::raise
end
end
}
    # If it's not a class or module, it's an instance
    begin
      module_eval(str, __FILE__, line_no)
    rescue
      instance_eval(str, __FILE__, line_no)
    end

  end

  alias delegate instance_delegate
  alias def_delegators def_instance_delegators
  alias def_delegator def_instance_delegator
end

#
# Usage of The SingleForwardable is like Fowadable module.
#
module SingleForwardable
  # 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.
  #
  # :call-seq:
  # delegate method => accessor
  # delegate [method, method, ...] => accessor
  #
  def single_delegate(hash)
    hash.each{ |methods, accessor|
      methods = methods.to_s unless methods.respond_to?(:each)
      methods.each{ |method|
        def_single_delegator(accessor, method)
      }
    }
  end

  #
  # 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(accessor, *methods)
    methods.delete("__send__")
    methods.delete("__id__")
    for method in methods
      def_single_delegator(accessor, method)
    end
  end

  #
  # Defines a method _method_ which delegates to _obj_ (i.e. it calls
  # the method of the same name in _obj_). If _new_name_ is
  # provided, it is used as the name for the delegate method.
  #
  def def_single_delegator(accessor, method, ali = method)
    line_no = __LINE__; str = %{
def #{ali}(*args, &block)
begin
#{accessor}.__send__(:#{method}, *args, &block)
rescue Exception
$@.delete_if{|s| %r"#{Regexp.quote(__FILE__)}"o =~ s} unless Forwardable::debug
::Kernel::raise
end
end
}

    instance_eval(str, __FILE__, __LINE__)
  end

  alias delegate single_delegate
  alias def_delegators def_single_delegators
  alias def_delegator def_single_delegator
end
Something went wrong with that request. Please try again.