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