Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 312 lines (294 sloc) 8.023 kb
511dc44 initial import
Laurent Sansonetti authored
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
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
40 #
511dc44 initial import
Laurent Sansonetti authored
41 # def stats( records )
42 # @source.__setobj__(records)
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
43 #
511dc44 initial import
Laurent Sansonetti authored
44 # "Elements: #{@source.size}\n" +
45 # " Non-Nil: #{@source.compact.size}\n" +
46 # " Unique: #{@source.uniq.size}\n"
47 # end
48 # end
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
49 #
511dc44 initial import
Laurent Sansonetti authored
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
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
60 #
511dc44 initial import
Laurent Sansonetti authored
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...
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
75 #
511dc44 initial import
Laurent Sansonetti authored
76 # def initialize(basename, tmpdir=Dir::tmpdir)
77 # # build up file path/name in var tmpname...
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
78 #
511dc44 initial import
Laurent Sansonetti authored
79 # @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
80 #
511dc44 initial import
Laurent Sansonetti authored
81 # # ...
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
82 #
511dc44 initial import
Laurent Sansonetti authored
83 # super(@tmpfile)
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
84 #
511dc44 initial import
Laurent Sansonetti authored
85 # # below this point, all methods of File are supported...
86 # end
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
87 #
511dc44 initial import
Laurent Sansonetti authored
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 # @delegate_sd_obj = obj # store obj for future use
99 # end
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
100 #
511dc44 initial import
Laurent Sansonetti authored
101 # def __getobj__
102 # @delegate_sd_obj # return object we are delegating to, required
103 # end
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
104 #
511dc44 initial import
Laurent Sansonetti authored
105 # def __setobj__(obj)
106 # @delegate_sd_obj = obj # change delegation object, a feature we're providing
107 # end
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
108 #
511dc44 initial import
Laurent Sansonetti authored
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 #
117 class Delegator
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
118 [:to_s,:inspect,:=~,:!~,:===].each do |m|
511dc44 initial import
Laurent Sansonetti authored
119 undef_method m
120 end
121
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
122 #
123 # Pass in the _obj_ to delegate method calls to. All methods supported by
124 # _obj_ will be delegated to.
125 #
126 def initialize(obj)
127 __setobj__(obj)
128 end
511dc44 initial import
Laurent Sansonetti authored
129
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
130 # Handles the magic of delegation through \_\_getobj\_\_.
131 def method_missing(m, *args, &block)
132 begin
133 target = self.__getobj__
134 unless target.respond_to?(m)
135 super(m, *args, &block)
136 else
137 target.__send__(m, *args, &block)
511dc44 initial import
Laurent Sansonetti authored
138 end
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
139 rescue Exception
140 $@.delete_if{|s| %r"\A#{__FILE__}:\d+:in `method_missing'\z"o =~ s}
141 ::Kernel::raise
511dc44 initial import
Laurent Sansonetti authored
142 end
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
143 end
511dc44 initial import
Laurent Sansonetti authored
144
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
145 #
146 # Checks for a method provided by this the delegate object by fowarding the
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
147 # call through \_\_getobj\_\_.
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
148 #
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
149 def respond_to?(m, include_private = false)
150 return true if super
151 return self.__getobj__.respond_to?(m, include_private)
152 end
511dc44 initial import
Laurent Sansonetti authored
153
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
154 #
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
155 # Returns true if two objects are considered same.
4b368bd @Watson1978 stdlib: Deleted the whitespece of end of line, and fixed the petty typo.
Watson1978 authored
156 #
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
157 def ==(obj)
158 return true if obj.equal?(self)
159 self.__getobj__ == obj
160 end
511dc44 initial import
Laurent Sansonetti authored
161
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
162 #
163 # This method must be overridden by subclasses and should return the object
164 # method calls are being delegated to.
165 #
166 def __getobj__
167 raise NotImplementedError, "need to define `__getobj__'"
168 end
511dc44 initial import
Laurent Sansonetti authored
169
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
170 #
171 # This method must be overridden by subclasses and change the object delegate
172 # to _obj_.
173 #
174 def __setobj__(obj)
175 raise NotImplementedError, "need to define `__setobj__'"
176 end
511dc44 initial import
Laurent Sansonetti authored
177
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
178 # Serialization support for the object returned by \_\_getobj\_\_.
179 def marshal_dump
180 __getobj__
181 end
182 # Reinitializes delegation from a serialized object.
183 def marshal_load(obj)
184 __setobj__(obj)
185 end
511dc44 initial import
Laurent Sansonetti authored
186
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
187 # Clone support for the object returned by \_\_getobj\_\_.
188 def clone
189 new = super
190 new.__setobj__(__getobj__.clone)
191 new
192 end
193 # Duplication support for the object returned by \_\_getobj\_\_.
194 def dup
195 new = super
196 new.__setobj__(__getobj__.dup)
197 new
511dc44 initial import
Laurent Sansonetti authored
198 end
199 end
200
201 #
202 # A concrete implementation of Delegator, this class provides the means to
203 # delegate all supported method calls to the object passed into the constructor
204 # and even to change the object being delegated to at a later time with
205 # \_\_setobj\_\_ .
206 #
207 class SimpleDelegator<Delegator
208 # Returns the current object method calls are being delegated to.
209 def __getobj__
210 @delegate_sd_obj
211 end
212
213 #
214 # Changes the delegate object to _obj_.
215 #
216 # It's important to note that this does *not* cause SimpleDelegator's methods
217 # to change. Because of this, you probably only want to change delegation
218 # to objects of the same type as the original delegate.
219 #
220 # Here's an example of changing the delegation object.
221 #
222 # names = SimpleDelegator.new(%w{James Edward Gray II})
223 # puts names[1] # => Edward
224 # names.__setobj__(%w{Gavin Sinclair})
225 # puts names[1] # => Sinclair
226 #
227 def __setobj__(obj)
228 raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
229 @delegate_sd_obj = obj
230 end
231 end
232
233 # :stopdoc:
234 def Delegator.delegating_block(mid)
235 lambda do |*args, &block|
236 begin
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
237 __getobj__.__send__(mid, *args, &block)
511dc44 initial import
Laurent Sansonetti authored
238 rescue
239 re = /\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}:/o
240 $!.backtrace.delete_if {|t| re =~ t}
241 raise
242 end
243 end
244 end
245 # :startdoc:
246
247 #
248 # The primary interface to this library. Use to setup delegation when defining
249 # your class.
250 #
251 # class MyClass < DelegateClass( ClassToDelegateTo ) # Step 1
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
252 # def initialize
511dc44 initial import
Laurent Sansonetti authored
253 # super(obj_of_ClassToDelegateTo) # Step 2
254 # end
255 # end
256 #
257 def DelegateClass(superclass)
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
258 klass = Class.new(Delegator)
511dc44 initial import
Laurent Sansonetti authored
259 methods = superclass.public_instance_methods(true)
8f21162 @richkilmer bring lib up to r22701 (ruby 1.9.1_0 tag). there are build issues using...
richkilmer authored
260 methods -= ::Delegator.public_instance_methods
261 methods -= [:to_s,:inspect,:=~,:!~,:===]
511dc44 initial import
Laurent Sansonetti authored
262 klass.module_eval {
263 def __getobj__ # :nodoc:
264 @delegate_dc_obj
265 end
266 def __setobj__(obj) # :nodoc:
267 raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
268 @delegate_dc_obj = obj
269 end
270 }
271 klass.module_eval do
272 methods.each do |method|
273 define_method(method, Delegator.delegating_block(method))
274 end
275 end
276 return klass
277 end
278
279 # :enddoc:
280
281 if __FILE__ == $0
282 class ExtArray<DelegateClass(Array)
283 def initialize()
284 super([])
285 end
286 end
287
288 ary = ExtArray.new
289 p ary.class
290 ary.push 25
291 p ary
292 ary.push 42
293 ary.each {|x| p x}
294
295 foo = Object.new
296 def foo.test
297 25
298 end
299 def foo.iter
300 yield self
301 end
302 def foo.error
303 raise 'this is OK'
304 end
305 foo2 = SimpleDelegator.new(foo)
306 p foo2
307 foo2.instance_eval{print "foo\n"}
308 p foo.test == foo2.test # => true
309 p foo2.iter{[55,true]} # => true
310 foo2.error # raise error!
311 end
Something went wrong with that request. Please try again.