/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. # Delegator is an abstract class used to build delegator pattern objects from
  113. # subclasses. Subclasses should redefine \_\_getobj\_\_. For a concrete
  114. # implementation, see SimpleDelegator.
  115. #
  116. class Delegator
  117. IgnoreBacktracePat = %r"\A#{Regexp.quote(__FILE__)}:\d+:in `"
  118. #
  119. # Pass in the _obj_ to delegate method calls to. All methods supported by
  120. # _obj_ will be delegated to.
  121. #
  122. def initialize(obj)
  123. preserved = ::Kernel.public_instance_methods(false)
  124. preserved -= ["to_s","to_a","inspect","==","=~","==="]
  125. for t in self.class.ancestors
  126. preserved |= t.public_instance_methods(false)
  127. preserved |= t.private_instance_methods(false)
  128. preserved |= t.protected_instance_methods(false)
  129. break if t == Delegator
  130. end
  131. preserved << "singleton_method_added"
  132. for method in obj.methods
  133. next if preserved.include? method
  134. begin
  135. eval <<-EOS, nil, __FILE__, __LINE__+1
  136. def self.#{method}(*args, &block)
  137. begin
  138. __getobj__.__send__(:#{method}, *args, &block)
  139. ensure
  140. $@.delete_if{|s|IgnoreBacktracePat=~s} if $@
  141. end
  142. end
  143. EOS
  144. rescue SyntaxError
  145. raise NameError, "invalid identifier %s" % method, caller(4)
  146. end
  147. end
  148. end
  149. alias initialize_methods initialize
  150. # Handles the magic of delegation through \_\_getobj\_\_.
  151. def method_missing(m, *args, &block)
  152. target = self.__getobj__
  153. unless target.respond_to?(m)
  154. super(m, *args, &block)
  155. end
  156. target.__send__(m, *args, &block)
  157. end
  158. #
  159. # Checks for a method provided by this the delegate object by fowarding the
  160. # call through \_\_getobj\_\_.
  161. #
  162. def respond_to?(m, include_private = false)
  163. return true if super
  164. return self.__getobj__.respond_to?(m, include_private)
  165. end
  166. #
  167. # This method must be overridden by subclasses and should return the object
  168. # method calls are being delegated to.
  169. #
  170. def __getobj__
  171. raise NotImplementedError, "need to define `__getobj__'"
  172. end
  173. # Serialization support for the object returned by \_\_getobj\_\_.
  174. def marshal_dump
  175. __getobj__
  176. end
  177. # Reinitializes delegation from a serialized object.
  178. def marshal_load(obj)
  179. initialize_methods(obj)
  180. __setobj__(obj)
  181. end
  182. end
  183. #
  184. # A concrete implementation of Delegator, this class provides the means to
  185. # delegate all supported method calls to the object passed into the constructor
  186. # and even to change the object being delegated to at a later time with
  187. # \_\_setobj\_\_ .
  188. #
  189. class SimpleDelegator<Delegator
  190. # Pass in the _obj_ you would like to delegate method calls to.
  191. def initialize(obj)
  192. super
  193. @_sd_obj = obj
  194. end
  195. # Returns the current object method calls are being delegated to.
  196. def __getobj__
  197. @_sd_obj
  198. end
  199. #
  200. # Changes the delegate object to _obj_.
  201. #
  202. # It's important to note that this does *not* cause SimpleDelegator's methods
  203. # to change. Because of this, you probably only want to change delegation
  204. # to objects of the same type as the original delegate.
  205. #
  206. # Here's an example of changing the delegation object.
  207. #
  208. # names = SimpleDelegator.new(%w{James Edward Gray II})
  209. # puts names[1] # => Edward
  210. # names.__setobj__(%w{Gavin Sinclair})
  211. # puts names[1] # => Sinclair
  212. #
  213. def __setobj__(obj)
  214. raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
  215. @_sd_obj = obj
  216. end
  217. # Clone support for the object returned by \_\_getobj\_\_.
  218. def clone
  219. new = super
  220. new.__setobj__(__getobj__.clone)
  221. new
  222. end
  223. # Duplication support for the object returned by \_\_getobj\_\_.
  224. def dup
  225. new = super
  226. new.__setobj__(__getobj__.clone)
  227. new
  228. end
  229. end
  230. # :stopdoc:
  231. # backward compatibility ^_^;;;
  232. Delegater = Delegator
  233. SimpleDelegater = SimpleDelegator
  234. # :startdoc:
  235. #
  236. # The primary interface to this library. Use to setup delegation when defining
  237. # your class.
  238. #
  239. # class MyClass < DelegateClass( ClassToDelegateTo ) # Step 1
  240. # def initialize
  241. # super(obj_of_ClassToDelegateTo) # Step 2
  242. # end
  243. # end
  244. #
  245. def DelegateClass(superclass)
  246. klass = Class.new
  247. methods = superclass.public_instance_methods(true)
  248. methods -= ::Kernel.public_instance_methods(false)
  249. methods |= ["to_s","to_a","inspect","==","=~","==="]
  250. klass.module_eval {
  251. def initialize(obj) # :nodoc:
  252. @_dc_obj = obj
  253. end
  254. def method_missing(m, *args, &block) # :nodoc:
  255. unless @_dc_obj.respond_to?(m)
  256. super(m, *args, &block)
  257. end
  258. @_dc_obj.__send__(m, *args, &block)
  259. end
  260. def respond_to?(m, include_private = false) # :nodoc:
  261. return true if super
  262. return @_dc_obj.respond_to?(m, include_private)
  263. end
  264. def __getobj__ # :nodoc:
  265. @_dc_obj
  266. end
  267. def __setobj__(obj) # :nodoc:
  268. raise ArgumentError, "cannot delegate to self" if self.equal?(obj)
  269. @_dc_obj = obj
  270. end
  271. def clone # :nodoc:
  272. new = super
  273. new.__setobj__(__getobj__.clone)
  274. new
  275. end
  276. def dup # :nodoc:
  277. new = super
  278. new.__setobj__(__getobj__.clone)
  279. new
  280. end
  281. }
  282. for method in methods
  283. begin
  284. klass.module_eval <<-EOS, __FILE__, __LINE__+1
  285. def #{method}(*args, &block)
  286. begin
  287. @_dc_obj.__send__(:#{method}, *args, &block)
  288. ensure
  289. $@.delete_if{|s| ::Delegator::IgnoreBacktracePat =~ s} if $@
  290. end
  291. end
  292. EOS
  293. rescue SyntaxError
  294. raise NameError, "invalid identifier %s" % method, caller(3)
  295. end
  296. end
  297. return klass
  298. end
  299. # :enddoc:
  300. if __FILE__ == $0
  301. class ExtArray<DelegateClass(Array)
  302. def initialize()
  303. super([])
  304. end
  305. end
  306. ary = ExtArray.new
  307. p ary.class
  308. ary.push 25
  309. p ary
  310. foo = Object.new
  311. def foo.test
  312. 25
  313. end
  314. def foo.error
  315. raise 'this is OK'
  316. end
  317. foo2 = SimpleDelegator.new(foo)
  318. p foo.test == foo2.test # => true
  319. foo2.error # raise error!
  320. end