PageRenderTime 68ms CodeModel.GetById 20ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/delegate.rb

http://github.com/agross/netopenspace
Ruby | 337 lines | 175 code | 12 blank | 150 comment | 15 complexity | 0e720963980b7a9cc8221e0b2106f723 MD5 | raw file
  1# = delegate -- Support for the Delegation Pattern
  2#
  3# Documentation by James Edward Gray II and Gavin Sinclair
  4#
  5# == Introduction
  6#
  7# This library provides three different ways to delegate method calls to an
  8# object.  The easiest to use is SimpleDelegator.  Pass an object to the
  9# constructor and all methods supported by the object will be delegated.  This
 10# object can be changed later.
 11#
 12# Going a step further, the top level DelegateClass method allows you to easily
 13# setup delegation through class inheritance.  This is considerably more
 14# flexible and thus probably the most common use for this library.
 15#
 16# Finally, if you need full control over the delegation scheme, you can inherit
 17# from the abstract class Delegator and customize as needed.  (If you find
 18# yourself needing this control, have a look at _forwardable_, also in the
 19# standard library.  It may suit your needs better.)
 20#
 21# == Notes
 22#
 23# Be advised, RDoc will not detect delegated methods.
 24#
 25# <b>delegate.rb provides full-class delegation via the
 26# DelegateClass() method.  For single-method delegation via
 27# def_delegator(), see forwardable.rb.</b>
 28#
 29# == Examples
 30#
 31# === SimpleDelegator
 32#
 33# Here's a simple example that takes advantage of the fact that
 34# SimpleDelegator's delegation object can be changed at any time.
 35#
 36#   class Stats
 37#     def initialize
 38#       @source = SimpleDelegator.new([])
 39#     end
 40#     
 41#     def stats( records )
 42#       @source.__setobj__(records)
 43#       	
 44#       "Elements:  #{@source.size}\n" +
 45#       " Non-Nil:  #{@source.compact.size}\n" +
 46#       "  Unique:  #{@source.uniq.size}\n"
 47#     end
 48#   end
 49#   
 50#   s = Stats.new
 51#   puts s.stats(%w{James Edward Gray II})
 52#   puts
 53#   puts s.stats([1, 2, 3, nil, 4, 5, 1, 2])
 54#
 55# <i>Prints:</i>
 56#
 57#   Elements:  4
 58#    Non-Nil:  4
 59#     Unique:  4
 60# 
 61#   Elements:  8
 62#    Non-Nil:  7
 63#     Unique:  6
 64#
 65# === DelegateClass()
 66#
 67# Here's a sample of use from <i>tempfile.rb</i>.
 68#
 69# A _Tempfile_ object is really just a _File_ object with a few special rules
 70# about storage location and/or when the File should be deleted.  That makes for
 71# an almost textbook perfect example of how to use delegation.
 72#
 73#   class Tempfile < DelegateClass(File)
 74#     # constant and class member data initialization...
 75#   
 76#     def initialize(basename, tmpdir=Dir::tmpdir)
 77#       # build up file path/name in var tmpname...
 78#     
 79#       @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
 80#     
 81#       # ...
 82#     
 83#       super(@tmpfile)
 84#     
 85#       # below this point, all methods of File are supported...
 86#     end
 87#   
 88#     # ...
 89#   end
 90#
 91# === Delegator
 92#
 93# SimpleDelegator's implementation serves as a nice example here.
 94#
 95#    class SimpleDelegator < Delegator
 96#      def initialize(obj)
 97#        super             # pass obj to Delegator constructor, required
 98#        @_sd_obj = obj    # store obj for future use
 99#      end
100# 
101#      def __getobj__
102#        @_sd_obj          # return object we are delegating to, required
103#      end
104# 
105#      def __setobj__(obj)
106#        @_sd_obj = obj    # change delegation object, a feature we're providing
107#      end
108# 
109#      # ...
110#    end
111
112#
113# Delegator is an abstract class used to build delegator pattern objects from
114# subclasses.  Subclasses should redefine \_\_getobj\_\_.  For a concrete
115# implementation, see SimpleDelegator.
116#
117class Delegator
118  IgnoreBacktracePat = %r"\A#{Regexp.quote(__FILE__)}:\d+:in `"
119
120  #
121  # Pass in the _obj_ to delegate method calls to.  All methods supported by
122  # _obj_ will be delegated to.
123  #
124  def initialize(obj)
125    preserved = ::Kernel.public_instance_methods(false)
126    preserved -= ["to_s","to_a","inspect","==","=~","==="]
127    for t in self.class.ancestors
128      preserved |= t.public_instance_methods(false)
129      preserved |= t.private_instance_methods(false)
130      preserved |= t.protected_instance_methods(false)
131      break if t == Delegator
132    end
133    preserved << "singleton_method_added"
134    for method in obj.methods
135      next if preserved.include? method
136      begin
137	eval <<-EOS, nil, __FILE__, __LINE__+1
138	  def self.#{method}(*args, &block)
139	    begin
140	      __getobj__.__send__(:#{method}, *args, &block)
141	    ensure
142	      $@.delete_if{|s|IgnoreBacktracePat=~s} if $@
143	    end
144	  end
145	EOS
146      rescue SyntaxError
147        raise NameError, "invalid identifier %s" % method, caller(4)
148      end
149    end
150  end
151  alias initialize_methods initialize
152
153  # Handles the magic of delegation through \_\_getobj\_\_.
154  def method_missing(m, *args, &block)
155    target = self.__getobj__
156    unless target.respond_to?(m)
157      super(m, *args, &block)
158    end
159    target.__send__(m, *args, &block)
160  end
161
162  # 
163  # Checks for a method provided by this the delegate object by fowarding the 
164  # call through \_\_getobj\_\_.
165  # 
166  def respond_to?(m, include_private = false)
167    return true if super
168    return self.__getobj__.respond_to?(m, include_private)
169  end
170
171  #
172  # This method must be overridden by subclasses and should return the object
173  # method calls are being delegated to.
174  #
175  def __getobj__
176    raise NotImplementedError, "need to define `__getobj__'"
177  end
178
179  # Serialization support for the object returned by \_\_getobj\_\_.
180  def marshal_dump
181    __getobj__
182  end
183  # Reinitializes delegation from a serialized object.
184  def marshal_load(obj)
185    initialize_methods(obj)
186    __setobj__(obj)
187  end
188end
189
190#
191# A concrete implementation of Delegator, this class provides the means to
192# delegate all supported method calls to the object passed into the constructor
193# and even to change the object being delegated to at a later time with
194# \_\_setobj\_\_ .
195#
196class SimpleDelegator<Delegator
197
198  # Pass in the _obj_ you would like to delegate method calls to.
199  def initialize(obj)
200    super
201    @_sd_obj = obj
202  end
203
204  # Returns the current object method calls are being delegated to.
205  def __getobj__
206    @_sd_obj
207  end
208
209  #
210  # Changes the delegate object to _obj_.
211  #
212  # It's important to note that this does *not* cause SimpleDelegator's methods
213  # to change.  Because of this, you probably only want to change delegation
214  # to objects of the same type as the original delegate.
215  #
216  # Here's an example of changing the delegation object.
217  #
218  #   names = SimpleDelegator.new(%w{James Edward Gray II})
219  #   puts names[1]    # => Edward
220  #   names.__setobj__(%w{Gavin Sinclair})
221  #   puts names[1]    # => Sinclair
222  #
223  def __setobj__(obj)
224    raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
225    @_sd_obj = obj
226  end
227
228  # Clone support for the object returned by \_\_getobj\_\_.
229  def clone
230    new = super
231    new.__setobj__(__getobj__.clone)
232    new
233  end
234  # Duplication support for the object returned by \_\_getobj\_\_.
235  def dup
236    new = super
237    new.__setobj__(__getobj__.clone)
238    new
239  end
240end
241
242# :stopdoc:
243# backward compatibility ^_^;;;
244Delegater = Delegator
245SimpleDelegater = SimpleDelegator
246# :startdoc:
247
248#
249# The primary interface to this library.  Use to setup delegation when defining
250# your class.
251#
252#   class MyClass < DelegateClass( ClassToDelegateTo )    # Step 1
253#     def initialize
254#       super(obj_of_ClassToDelegateTo)                   # Step 2
255#     end
256#   end
257#
258def DelegateClass(superclass)
259  klass = Class.new
260  methods = superclass.public_instance_methods(true)
261  methods -= ::Kernel.public_instance_methods(false)
262  methods |= ["to_s","to_a","inspect","==","=~","==="]
263  klass.module_eval {
264    def initialize(obj)  # :nodoc:
265      @_dc_obj = obj
266    end
267    def method_missing(m, *args, &block)  # :nodoc:
268      unless @_dc_obj.respond_to?(m)
269        super(m, *args, &block)
270      end
271      @_dc_obj.__send__(m, *args, &block)
272    end
273    def respond_to?(m, include_private = false)  # :nodoc:
274      return true if super
275      return @_dc_obj.respond_to?(m, include_private)
276    end
277    def __getobj__  # :nodoc:
278      @_dc_obj
279    end
280    def __setobj__(obj)  # :nodoc:
281      raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
282      @_dc_obj = obj
283    end
284    def clone  # :nodoc:
285      new = super
286      new.__setobj__(__getobj__.clone)
287      new
288    end
289    def dup  # :nodoc:
290      new = super
291      new.__setobj__(__getobj__.clone)
292      new
293    end
294  }
295  for method in methods
296    begin
297      klass.module_eval <<-EOS, __FILE__, __LINE__+1
298        def #{method}(*args, &block)
299	  begin
300	    @_dc_obj.__send__(:#{method}, *args, &block)
301	  ensure
302	    $@.delete_if{|s| ::Delegator::IgnoreBacktracePat =~ s} if $@
303	  end
304	end
305      EOS
306    rescue SyntaxError
307      raise NameError, "invalid identifier %s" % method, caller(3)
308    end
309  end
310  return klass
311end
312
313# :enddoc:
314
315if __FILE__ == $0
316  class ExtArray<DelegateClass(Array)
317    def initialize()
318      super([])
319    end
320  end
321
322  ary = ExtArray.new
323  p ary.class
324  ary.push 25
325  p ary
326
327  foo = Object.new
328  def foo.test
329    25
330  end
331  def foo.error
332    raise 'this is OK'
333  end
334  foo2 = SimpleDelegator.new(foo)
335  p foo.test == foo2.test	# => true
336  foo2.error			# raise error!
337end