module Forwardable
TheForwardable module provides delegation of specified methods to a designated object, using the methodsdef_delegator anddef_delegators.
For example, say you have a class RecordCollection which contains an array@records. You could provide the lookup method record_number(), which simply calls [] on the@records array, like this:
require'forwardable'classRecordCollectionattr_accessor:recordsextendForwardabledef_delegator:@records,:[],:record_numberend
We can use the lookup method like so:
r =RecordCollection.newr.records = [4,5,6]r.record_number(0)# => 4
Further, if you wish to provide the methods size,<<, and map, all of which delegate to @records, this is how you can do it:
classRecordCollection# re-open RecordCollection classdef_delegators:@records,:size,:<<,:mapendr =RecordCollection.newr.records = [1,2,3]r.record_number(0)# => 1r.size# => 3r<<4# => [1, 2, 3, 4]r.map {|x|x*2 }# => [2, 4, 6, 8]
You can even extend regular objects withForwardable.
my_hash =Hash.newmy_hash.extendForwardable# prepare object for delegationmy_hash.def_delegator"STDOUT","puts"# add delegation for STDOUT.puts()my_hash.puts"Howdy!"
Another example
You could useForwardable as an alternative to inheritance, when you don’t want to inherit all methods from the superclass. For instance, here is how you might add a range ofArray instance methods to a new classQueue:
classQueueextendForwardabledefinitialize@q = [ ]# prepare delegate objectend# setup preferred interface, enq() and deq()...def_delegator:@q,:push,:enqdef_delegator:@q,:shift,:deq# support some general Array methods that fit Queues welldef_delegators:@q,:clear,:first,:push,:shift,:sizeendq =Thread::Queue.newq.enq1,2,3,4,5q.push6q.shift# => 1whileq.size>0putsq.deqendq.enq"Ruby","Perl","Python"putsq.firstq.clearputsq.first
This should output:
23456Rubynil
Notes
Be advised, RDoc will not detect delegated methods.
forwardable.rb provides single-method delegation via thedef_delegator anddef_delegators methods. For full-class delegation via DelegateClass, seedelegate.rb.
Constants
- FORWARDABLE_VERSION
Version for backward compatibility
- VERSION
Version offorwardable.rb
Attributes
ignored
Public Instance Methods
Source
# File lib/forwardable.rb, line 188defdef_instance_delegator(accessor,method,ali =method)gen =Forwardable._delegator_method(self,accessor,method,ali)# If it's not a class or module, it's an instancemod =Module===self?self:singleton_classmod.module_eval(&gen)end
Definemethod as delegator instance method with an optional alias nameali. Method calls toali will be delegated toaccessor.method.accessor should be a method name, instance variable name, or constant name. Use the full path to the constant if providing the constant name. Returns the name of the method defined.
classMyQueueCONST =1extendForwardableattr_reader:queuedefinitialize@queue = []enddef_delegator:@queue,:push,:mypushdef_delegator'MyQueue::CONST',:to_iendq =MyQueue.newq.mypush42q.queue#=> [42]q.push23#=> NoMethodErrorq.to_i#=> 1
Source
# File lib/forwardable.rb, line 156defdef_instance_delegators(accessor,*methods)methods.eachdo|method|nextif/\A__(?:send|id)__\z/=~methoddef_instance_delegator(accessor,method)endend
Shortcut for defining multiple delegator methods, but with no provision for using a different name. The following two code samples have the same effect:
def_delegators:@records,:size,:<<,:mapdef_delegator:@records,:sizedef_delegator:@records,:<<def_delegator:@records,:map
Source
# File lib/forwardable.rb, line 135definstance_delegate(hash)hash.eachdo|methods,accessor|unlessdefined?(methods.each)def_instance_delegator(accessor,methods)elsemethods.each {|method|def_instance_delegator(accessor,method)}endendend