The Forwardable module provides delegation of specified methods to a designated object, using the methods def_delegator and def_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:
class RecordCollection
extend Forwardable
def_delegator :@records, :[], :record_number
end
Further, if you wish to provide the methods size, <<, and map, all of which delegate to @records, this is how you can do it:
class RecordCollection
# extend Forwardable, but we did that above
def_delegators :@records, :size, :<<, :map
end
f = Foo.new
f.printf ...
f.gets
f.content_at(1)
If the object isn't a Module and Class, You can too extend Forwardable module.
printer = String.new
printer.extend Forwardable # prepare object for delegation
printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts()
printer.puts "Howdy!"
Another example
We want to rely on what has come before obviously, but with delegation we can take just the methods we need and even rename them as appropriate. In many cases this is preferable to inheritance, which gives us the entire old interface, even if much of it isn't needed.
class Queue
extend Forwardable
def initialize
@q = [ ] # prepare delegate object
end
# setup preferred interface, enq() and deq()...
def_delegator :@q, :push, :enq
def_delegator :@q, :shift, :deq
# support some general Array methods that fit Queues well
def_delegators :@q, :clear, :first, :push, :shift, :size
end
q = Queue.new
q.enq 1, 2, 3, 4, 5
q.push 6
q.shift # => 1
while q.size > 0
puts q.deq
end
q.enq "Ruby", "Perl", "Python"
puts q.first
q.clear
puts q.first
This should output:
2
3
4
5
6
Ruby
nil
Notes
Be advised, RDoc will not detect delegated methods.
forwardable.rb
provides single-method delegation via the #def_delegator and #def_delegators
methods. For full-class delegation via DelegateClass, see
delegate.rb
.
- D
- I
FORWARDABLE_VERSION | = | "1.1.0" |
Version of |
[RW] | debug | If true, |
Define method
as delegator instance method with an optional
alias name ali
. Method calls to
ali
will be delegated to accessor.method
.
class MyQueue
extend Forwardable
attr_reader :queue
def initialize
@queue = []
end
def_delegator :@queue, :push, :mypush
end
q = MyQueue.new
q.mypush 42
q.queue #=> [42]
q.push 23 #=> NoMethodError
# File lib/forwardable.rb, line 167 def def_instance_delegator(accessor, method, ali = method) line_no = __LINE__; str = %Q{ def #{ali}(*args, &block) begin #{accessor}.__send__(:#{method}, *args, &block) rescue Exception $@.delete_if{|s| %r"#{Regexp.quote(__FILE__)}"o =~ s} unless Forwardable::debug ::Kernel::raise end end } # If it's not a class or module, it's an instance begin module_eval(str, __FILE__, line_no) rescue instance_eval(str, __FILE__, line_no) end end
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, :<<, :map
def_delegator :@records, :size
def_delegator :@records, :<<
def_delegator :@records, :map