Fix up Rubinius specific library specs.
[rbx.git] / lib / forwardable.rb
blob4f79916cf59aae52900e926122998c05dcc22d84
1 # = forwardable - Support for the Delegation Pattern
3 #    $Release Version: 1.1$
4 #    $Revision: 11708 $
5 #    $Date: 2007-02-12 15:01:19 -0800 (Mon, 12 Feb 2007) $
6 #    by Keiju ISHITSUKA(keiju@ishitsuka.com)
8 #    Documentation by James Edward Gray II and Gavin Sinclair
10 # == Introduction
12 # This library allows you delegate method calls to an object, on a method by
13 # method basis.  You can use Forwardable to setup this delegation at the class
14 # level, or SingleForwardable to handle it at the object level.
16 # == Notes
18 # Be advised, RDoc will not detect delegated methods.
20 # <b>forwardable.rb provides single-method delegation via the
21 # def_delegator() and def_delegators() methods.  For full-class
22 # delegation via DelegateClass(), see delegate.rb.</b>
24 # == Examples
26 # === Forwardable
28 # Forwardable makes building a new class based on existing work, with a proper
29 # interface, almost trivial.  We want to rely on what has come before obviously,
30 # but with delegation we can take just the methods we need and even rename them
31 # as appropriate.  In many cases this is preferable to inheritance, which gives
32 # us the entire old interface, even if much of it isn't needed.
34 #   class Queue
35 #     extend Forwardable
36 #     
37 #     def initialize
38 #       @q = [ ]    # prepare delegate object
39 #     end
40 #     
41 #     # setup prefered interface, enq() and deq()...
42 #     def_delegator :@q, :push, :enq
43 #     def_delegator :@q, :shift, :deq
44 #     
45 #     # support some general Array methods that fit Queues well
46 #     def_delegators :@q, :clear, :first, :push, :shift, :size
47 #   end
48
49 #   q = Queue.new
50 #   q.enq 1, 2, 3, 4, 5
51 #   q.push 6
52
53 #   q.shift    # => 1
54 #   while q.size > 0
55 #     puts q.deq
56 #   end
57
58 #   q.enq "Ruby", "Perl", "Python"
59 #   puts q.first
60 #   q.clear
61 #   puts q.first
63 # <i>Prints:</i>
65 #   2
66 #   3
67 #   4
68 #   5
69 #   6
70 #   Ruby
71 #   nil
73 # === SingleForwardable
75 #    printer = String.new
76 #    printer.extend SingleForwardable        # prepare object for delegation
77 #    printer.def_delegator "STDOUT", "puts"  # add delegation for STDOUT.puts()
78 #    printer.puts "Howdy!"
80 # <i>Prints:</i>
82 #    Howdy!
85 # The Forwardable module provides delegation of specified
86 # methods to a designated object, using the methods #def_delegator
87 # and #def_delegators.
89 # For example, say you have a class RecordCollection which
90 # contains an array <tt>@records</tt>.  You could provide the lookup method
91 # #record_number(), which simply calls #[] on the <tt>@records</tt>
92 # array, like this:
94 #   class RecordCollection
95 #     extend Forwardable
96 #     def_delegator :@records, :[], :record_number
97 #   end
99 # Further, if you wish to provide the methods #size, #<<, and #map,
100 # all of which delegate to @records, this is how you can do it:
102 #   class RecordCollection
103 #     # extend Forwardable, but we did that above
104 #     def_delegators :@records, :size, :<<, :map
105 #   end
107 # Also see the example at forwardable.rb.
109 module Forwardable
111   @debug = nil
112   class<<self
113     # force Forwardable to show up in stack backtraces of delegated methods
114     attr_accessor :debug
115   end
117   #
118   # Shortcut for defining multiple delegator methods, but with no
119   # provision for using a different name.  The following two code
120   # samples have the same effect:
121   #
122   #   def_delegators :@records, :size, :<<, :map
123   #
124   #   def_delegator :@records, :size
125   #   def_delegator :@records, :<<
126   #   def_delegator :@records, :map
127   #
128   # See the examples at Forwardable and forwardable.rb.
129   #
130   def def_instance_delegators(accessor, *methods)
131     for method in methods
132       def_instance_delegator(accessor, method)
133     end
134   end
136   #
137   # Defines a method _method_ which delegates to _obj_ (i.e. it calls
138   # the method of the same name in _obj_).  If _new_name_ is
139   # provided, it is used as the name for the delegate method.
140   #
141   # See the examples at Forwardable and forwardable.rb.
142   #
143   def def_instance_delegator(accessor, method, ali = method)
144     accessor = accessor.id2name if accessor.kind_of?(Integer)
145     method = method.id2name if method.kind_of?(Integer)
146     ali = ali.id2name if ali.kind_of?(Integer)
148     module_eval(<<-EOS, "(__FORWARDABLE__)", 1)
149       def #{ali}(*args, &block)
150         begin
151           #{accessor}.__send__(:#{method}, *args, &block)
152         rescue Exception
153           $@.delete_if{|s| /^\\(__FORWARDABLE__\\):/ =~ s} unless Forwardable::debug
154           Kernel::raise
155         end
156       end
157     EOS
158   end
160   alias def_delegators def_instance_delegators
161   alias def_delegator def_instance_delegator
165 # The SingleForwardable module provides delegation of specified
166 # methods to a designated object, using the methods #def_delegator
167 # and #def_delegators.  This module is similar to Forwardable, but it works on
168 # objects themselves, instead of their defining classes.
170 # Also see the example at forwardable.rb.
172 module SingleForwardable
173   #
174   # Shortcut for defining multiple delegator methods, but with no
175   # provision for using a different name.  The following two code
176   # samples have the same effect:
177   #
178   #   single_forwardable.def_delegators :@records, :size, :<<, :map
179   #
180   #   single_forwardable.def_delegator :@records, :size
181   #   single_forwardable.def_delegator :@records, :<<
182   #   single_forwardable.def_delegator :@records, :map
183   #
184   # See the example at forwardable.rb.
185   #
186   def def_singleton_delegators(accessor, *methods)
187     for method in methods
188       def_singleton_delegator(accessor, method)
189     end
190   end
192   #
193   # Defines a method _method_ which delegates to _obj_ (i.e. it calls
194   # the method of the same name in _obj_).  If _new_name_ is
195   # provided, it is used as the name for the delegate method.
196   #
197   # See the example at forwardable.rb.
198   #
199   def def_singleton_delegator(accessor, method, ali = method)
200     accessor = accessor.id2name if accessor.kind_of?(Integer)
201     method = method.id2name if method.kind_of?(Integer)
202     ali = ali.id2name if ali.kind_of?(Integer)
204     instance_eval(<<-EOS, "(__FORWARDABLE__)", 1)
205        def #{ali}(*args, &block)
206          begin
207            #{accessor}.__send__(:#{method}, *args,&block)
208          rescue Exception
209            $@.delete_if{|s| /^\\(__FORWARDABLE__\\):/ =~ s} unless Forwardable::debug
210            Kernel::raise
211          end
212        end
213     EOS
214   end
216   alias def_delegators def_singleton_delegators
217   alias def_delegator def_singleton_delegator