lib/delegate.rb


DEFINITIONS

This source file includes following functions.


   1  #  Delegation class that delegates even methods defined in super class,
   2  # which can not be covered with normal method_missing hack.
   3  #  
   4  #  Delegator is the abstract delegation class. Need to redefine
   5  # `__getobj__' method in the subclass.  SimpleDelegator is the 
   6  # concrete subclass for simple delegation.
   7  #
   8  # Usage:
   9  #   foo = Object.new
  10  #   foo2 = SimpleDelegator.new(foo)
  11  #   foo.hash == foo2.hash # => false
  12  #
  13  #   Foo = DelegateClass(Array)
  14  #
  15  #  class ExtArray<DelegateClass(Array)
  16  #    ...
  17  #  end
  18  
  19  class Delegator
  20  
  21    def initialize(obj)
  22      preserved = ::Kernel.instance_methods
  23      preserved -= ["to_s","to_a","inspect","==","=~","==="]
  24      for t in self.type.ancestors
  25        preserved |= t.instance_methods
  26        preserved |= t.private_instance_methods
  27        preserved |= t.protected_instance_methods
  28        break if t == Delegator
  29      end
  30      for method in obj.methods
  31        next if preserved.include? method
  32        begin
  33          eval <<-EOS
  34            def self.#{method}(*args, &block)
  35              begin
  36                __getobj__.__send__(:#{method}, *args, &block)
  37              rescue Exception
  38                $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
  39                $@.delete_if{|s| /^\\(eval\\):/ =~ s}
  40                raise
  41              end
  42            end
  43          EOS
  44        rescue SyntaxError
  45          raise NameError, "invalid identifier %s" % method, caller(4)
  46        end
  47      end
  48    end
  49  
  50    def __getobj__
  51      raise NotImplementError, "need to define `__getobj__'"
  52    end
  53  
  54  end
  55  
  56  class SimpleDelegator<Delegator
  57  
  58    def initialize(obj)
  59      super
  60      @obj = obj
  61    end
  62  
  63    def __getobj__
  64      @obj
  65    end
  66  
  67    def __setobj__(obj)
  68      @obj = obj
  69    end
  70  end
  71  
  72  # backward compatibility ^_^;;;
  73  Delegater = Delegator
  74  SimpleDelegater = SimpleDelegator
  75  
  76  #
  77  def DelegateClass(superclass)
  78    klass = Class.new
  79    methods = superclass.instance_methods(true)
  80    methods -= ::Kernel.instance_methods
  81    methods |= ["to_s","to_a","inspect","==","=~","==="]
  82    klass.module_eval <<-EOS
  83    def initialize(obj)
  84      @obj = obj
  85    end
  86    EOS
  87    for method in methods
  88      begin
  89        klass.module_eval <<-EOS
  90          def #{method}(*args, &block)
  91            begin
  92              @obj.__send__(:#{method}, *args, &block)
  93            rescue
  94              $@[0,2] = nil
  95              raise
  96            end
  97          end
  98        EOS
  99      rescue SyntaxError
 100        raise NameError, "invalid identifier %s" % method, caller(3)
 101      end
 102    end
 103    return klass;
 104  end
 105  
 106  if __FILE__ == $0
 107    class ExtArray<DelegateClass(Array)
 108      def initialize()
 109        super([])
 110      end
 111    end
 112  
 113    ary = ExtArray.new
 114    p ary.type
 115    ary.push 25
 116    p ary
 117  
 118    foo = Object.new
 119    def foo.test
 120      25
 121    end
 122    def foo.error
 123      raise 'this is OK'
 124    end
 125    foo2 = SimpleDelegator.new(foo)
 126    p foo.test == foo2.test       # => true
 127    foo2.error                    # raise error!
 128  end