[rubygems/rubygems] Use a constant empty tar header to avoid extra allocations
[ruby.git] / lib / delegate.rb
blob1ea4fb985b544d25793c269b77935d8ee8ea044c
1 # frozen_string_literal: true
2 # = delegate -- Support for the Delegation Pattern
4 # Documentation by James Edward Gray II and Gavin Sinclair
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.
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.
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 which is also in
19 # the standard library.  It may suit your needs better.)
21 # SimpleDelegator's implementation serves as a nice example of the use of
22 # Delegator:
24 #   require 'delegate'
26 #   class SimpleDelegator < Delegator
27 #     def __getobj__
28 #       @delegate_sd_obj # return object we are delegating to, required
29 #     end
31 #     def __setobj__(obj)
32 #       @delegate_sd_obj = obj # change delegation object,
33 #                              # a feature we're providing
34 #     end
35 #   end
37 # == Notes
39 # Be advised, RDoc will not detect delegated methods.
41 class Delegator < BasicObject
42   VERSION = "0.3.1"
44   kernel = ::Kernel.dup
45   kernel.class_eval do
46     alias __raise__ raise
47     [:to_s, :inspect, :!~, :===, :<=>, :hash].each do |m|
48       undef_method m
49     end
50     private_instance_methods.each do |m|
51       if /\Ablock_given\?\z|\Aiterator\?\z|\A__.*__\z/ =~ m
52         next
53       end
54       undef_method m
55     end
56   end
57   include kernel
59   # :stopdoc:
60   def self.const_missing(n)
61     ::Object.const_get(n)
62   end
63   # :startdoc:
65   ##
66   # :method: raise
67   # Use #__raise__ if your Delegator does not have a object to delegate the
68   # #raise method call.
69   #
71   #
72   # Pass in the _obj_ to delegate method calls to.  All methods supported by
73   # _obj_ will be delegated to.
74   #
75   def initialize(obj)
76     __setobj__(obj)
77   end
79   #
80   # Handles the magic of delegation through \_\_getobj\_\_.
81   #
82   ruby2_keywords def method_missing(m, *args, &block)
83     r = true
84     target = self.__getobj__ {r = false}
86     if r && target_respond_to?(target, m, false)
87       target.__send__(m, *args, &block)
88     elsif ::Kernel.method_defined?(m) || ::Kernel.private_method_defined?(m)
89       ::Kernel.instance_method(m).bind_call(self, *args, &block)
90     else
91       super(m, *args, &block)
92     end
93   end
95   #
96   # Checks for a method provided by this the delegate object by forwarding the
97   # call through \_\_getobj\_\_.
98   #
99   def respond_to_missing?(m, include_private)
100     r = true
101     target = self.__getobj__ {r = false}
102     r &&= target_respond_to?(target, m, include_private)
103     if r && include_private && !target_respond_to?(target, m, false)
104       warn "delegator does not forward private method \##{m}", uplevel: 3
105       return false
106     end
107     r
108   end
110   KERNEL_RESPOND_TO = ::Kernel.instance_method(:respond_to?)
111   private_constant :KERNEL_RESPOND_TO
113   # Handle BasicObject instances
114   private def target_respond_to?(target, m, include_private)
115     case target
116     when Object
117       target.respond_to?(m, include_private)
118     else
119       if KERNEL_RESPOND_TO.bind_call(target, :respond_to?)
120         target.respond_to?(m, include_private)
121       else
122         KERNEL_RESPOND_TO.bind_call(target, m, include_private)
123       end
124     end
125   end
127   #
128   # Returns the methods available to this delegate object as the union
129   # of this object's and \_\_getobj\_\_ methods.
130   #
131   def methods(all=true)
132     __getobj__.methods(all) | super
133   end
135   #
136   # Returns the methods available to this delegate object as the union
137   # of this object's and \_\_getobj\_\_ public methods.
138   #
139   def public_methods(all=true)
140     __getobj__.public_methods(all) | super
141   end
143   #
144   # Returns the methods available to this delegate object as the union
145   # of this object's and \_\_getobj\_\_ protected methods.
146   #
147   def protected_methods(all=true)
148     __getobj__.protected_methods(all) | super
149   end
151   # Note: no need to specialize private_methods, since they are not forwarded
153   #
154   # Returns true if two objects are considered of equal value.
155   #
156   def ==(obj)
157     return true if obj.equal?(self)
158     self.__getobj__ == obj
159   end
161   #
162   # Returns true if two objects are not considered of equal value.
163   #
164   def !=(obj)
165     return false if obj.equal?(self)
166     __getobj__ != obj
167   end
169   #
170   # Returns true if two objects are considered of equal value.
171   #
172   def eql?(obj)
173     return true if obj.equal?(self)
174     obj.eql?(__getobj__)
175   end
177   #
178   # Delegates ! to the \_\_getobj\_\_
179   #
180   def !
181     !__getobj__
182   end
184   #
185   # This method must be overridden by subclasses and should return the object
186   # method calls are being delegated to.
187   #
188   def __getobj__
189     __raise__ ::NotImplementedError, "need to define '__getobj__'"
190   end
192   #
193   # This method must be overridden by subclasses and change the object delegate
194   # to _obj_.
195   #
196   def __setobj__(obj)
197     __raise__ ::NotImplementedError, "need to define '__setobj__'"
198   end
200   #
201   # Serialization support for the object returned by \_\_getobj\_\_.
202   #
203   def marshal_dump
204     ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var}
205     [
206       :__v2__,
207       ivars, ivars.map {|var| instance_variable_get(var)},
208       __getobj__
209     ]
210   end
212   #
213   # Reinitializes delegation from a serialized object.
214   #
215   def marshal_load(data)
216     version, vars, values, obj = data
217     if version == :__v2__
218       vars.each_with_index {|var, i| instance_variable_set(var, values[i])}
219       __setobj__(obj)
220     else
221       __setobj__(data)
222     end
223   end
225   def initialize_clone(obj, freeze: nil) # :nodoc:
226     self.__setobj__(obj.__getobj__.clone(freeze: freeze))
227   end
228   def initialize_dup(obj) # :nodoc:
229     self.__setobj__(obj.__getobj__.dup)
230   end
231   private :initialize_clone, :initialize_dup
233   ##
234   # :method: freeze
235   # Freeze both the object returned by \_\_getobj\_\_ and self.
236   #
237   def freeze
238     __getobj__.freeze
239     super()
240   end
242   @delegator_api = self.public_instance_methods
243   def self.public_api # :nodoc:
244     @delegator_api
245   end
249 # A concrete implementation of Delegator, this class provides the means to
250 # delegate all supported method calls to the object passed into the constructor
251 # and even to change the object being delegated to at a later time with
252 # #__setobj__.
254 #   class User
255 #     def born_on
256 #       Date.new(1989, 9, 10)
257 #     end
258 #   end
260 #   require 'delegate'
262 #   class UserDecorator < SimpleDelegator
263 #     def birth_year
264 #       born_on.year
265 #     end
266 #   end
268 #   decorated_user = UserDecorator.new(User.new)
269 #   decorated_user.birth_year  #=> 1989
270 #   decorated_user.__getobj__  #=> #<User: ...>
272 # A SimpleDelegator instance can take advantage of the fact that SimpleDelegator
273 # is a subclass of +Delegator+ to call <tt>super</tt> to have methods called on
274 # the object being delegated to.
276 #   class SuperArray < SimpleDelegator
277 #     def [](*args)
278 #       super + 1
279 #     end
280 #   end
282 #   SuperArray.new([1])[0]  #=> 2
284 # Here's a simple example that takes advantage of the fact that
285 # SimpleDelegator's delegation object can be changed at any time.
287 #   class Stats
288 #     def initialize
289 #       @source = SimpleDelegator.new([])
290 #     end
292 #     def stats(records)
293 #       @source.__setobj__(records)
295 #       "Elements:  #{@source.size}\n" +
296 #       " Non-Nil:  #{@source.compact.size}\n" +
297 #       "  Unique:  #{@source.uniq.size}\n"
298 #     end
299 #   end
301 #   s = Stats.new
302 #   puts s.stats(%w{James Edward Gray II})
303 #   puts
304 #   puts s.stats([1, 2, 3, nil, 4, 5, 1, 2])
306 # Prints:
308 #   Elements:  4
309 #    Non-Nil:  4
310 #     Unique:  4
312 #   Elements:  8
313 #    Non-Nil:  7
314 #     Unique:  6
316 class SimpleDelegator < Delegator
317   # Returns the current object method calls are being delegated to.
318   def __getobj__
319     unless defined?(@delegate_sd_obj)
320       return yield if block_given?
321       __raise__ ::ArgumentError, "not delegated"
322     end
323     @delegate_sd_obj
324   end
326   #
327   # Changes the delegate object to _obj_.
328   #
329   # It's important to note that this does *not* cause SimpleDelegator's methods
330   # to change.  Because of this, you probably only want to change delegation
331   # to objects of the same type as the original delegate.
332   #
333   # Here's an example of changing the delegation object.
334   #
335   #   names = SimpleDelegator.new(%w{James Edward Gray II})
336   #   puts names[1]    # => Edward
337   #   names.__setobj__(%w{Gavin Sinclair})
338   #   puts names[1]    # => Sinclair
339   #
340   def __setobj__(obj)
341     __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
342     @delegate_sd_obj = obj
343   end
346 def Delegator.delegating_block(mid) # :nodoc:
347   lambda do |*args, &block|
348     target = self.__getobj__
349     target.__send__(mid, *args, &block)
350   end.ruby2_keywords
354 # The primary interface to this library.  Use to setup delegation when defining
355 # your class.
357 #   class MyClass < DelegateClass(ClassToDelegateTo) # Step 1
358 #     def initialize
359 #       super(obj_of_ClassToDelegateTo)              # Step 2
360 #     end
361 #   end
363 # or:
365 #   MyClass = DelegateClass(ClassToDelegateTo) do    # Step 1
366 #     def initialize
367 #       super(obj_of_ClassToDelegateTo)              # Step 2
368 #     end
369 #   end
371 # Here's a sample of use from Tempfile which is really a File object with a
372 # few special rules about storage location and when the File should be
373 # deleted.  That makes for an almost textbook perfect example of how to use
374 # delegation.
376 #   class Tempfile < DelegateClass(File)
377 #     # constant and class member data initialization...
379 #     def initialize(basename, tmpdir=Dir::tmpdir)
380 #       # build up file path/name in var tmpname...
382 #       @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
384 #       # ...
386 #       super(@tmpfile)
388 #       # below this point, all methods of File are supported...
389 #     end
391 #     # ...
392 #   end
394 def DelegateClass(superclass, &block)
395   klass = Class.new(Delegator)
396   ignores = [*::Delegator.public_api, :to_s, :inspect, :=~, :!~, :===]
397   protected_instance_methods = superclass.protected_instance_methods
398   protected_instance_methods -= ignores
399   public_instance_methods = superclass.public_instance_methods
400   public_instance_methods -= ignores
401   klass.module_eval do
402     def __getobj__ # :nodoc:
403       unless defined?(@delegate_dc_obj)
404         return yield if block_given?
405         __raise__ ::ArgumentError, "not delegated"
406       end
407       @delegate_dc_obj
408     end
409     def __setobj__(obj)  # :nodoc:
410       __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
411       @delegate_dc_obj = obj
412     end
413     protected_instance_methods.each do |method|
414       define_method(method, Delegator.delegating_block(method))
415       protected method
416     end
417     public_instance_methods.each do |method|
418       define_method(method, Delegator.delegating_block(method))
419     end
420   end
421   klass.define_singleton_method :public_instance_methods do |all=true|
422     super(all) | superclass.public_instance_methods
423   end
424   klass.define_singleton_method :protected_instance_methods do |all=true|
425     super(all) | superclass.protected_instance_methods
426   end
427   klass.define_singleton_method :instance_methods do |all=true|
428     super(all) | superclass.instance_methods
429   end
430   klass.define_singleton_method :public_instance_method do |name|
431     super(name)
432   rescue NameError
433     raise unless self.public_instance_methods.include?(name)
434     superclass.public_instance_method(name)
435   end
436   klass.define_singleton_method :instance_method do |name|
437     super(name)
438   rescue NameError
439     raise unless self.instance_methods.include?(name)
440     superclass.instance_method(name)
441   end
442   klass.module_eval(&block) if block
443   return klass