Module: Forwardable
- Defined in:
- lib/forwardable.rb
Overview
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:
require 'forwardable'
class RecordCollection
attr_accessor :records
extend Forwardable
def_delegator :@records, :[], :record_number
end
We can use the lookup method like so:
r = RecordCollection.new
r.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:
class RecordCollection # re-open RecordCollection class
def_delegators :@records, :size, :<<, :map
end
r = RecordCollection.new
r.records = [1,2,3]
r.record_number(0) # => 1
r.size # => 3
r << 4 # => [1, 2, 3, 4]
r.map { |x| x * 2 } # => [2, 4, 6, 8]
You can even extend regular objects with Forwardable.
my_hash = Hash.new
my_hash.extend Forwardable # prepare object for delegation
my_hash.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts()
my_hash.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
.
Constant Summary collapse
- FORWARDABLE_VERSION =
Version of
forwardable.rb
"1.1.0"
- FILE_REGEXP =
%r"#{Regexp.quote(__FILE__)}"
Class Attribute Summary collapse
-
.debug ⇒ Object
If true,
__FILE__
will remain in the backtrace in the event an Exception is raised.
Instance Method Summary collapse
-
#def_instance_delegator(accessor, method, ali = method) ⇒ Object
(also: #def_delegator)
Define
method
as delegator instance method with an optional alias nameali
. -
#def_instance_delegators(accessor, *methods) ⇒ Object
(also: #def_delegators)
Shortcut for defining multiple delegator methods, but with no provision for using a different name.
-
#instance_delegate(hash) ⇒ Object
(also: #delegate)
Takes a hash as its argument.
Class Attribute Details
.debug ⇒ Object
If true, __FILE__
will remain in the backtrace in the event an Exception is raised.
122 123 124 |
# File 'lib/forwardable.rb', line 122 def debug @debug end |
Instance Method Details
#def_instance_delegator(accessor, method, ali = method) ⇒ Object Also known as: def_delegator
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
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/forwardable.rb', line 180 def def_instance_delegator(accessor, method, ali = method) accessor = accessor.to_s if method_defined?(accessor) || private_method_defined?(accessor) accessor = "#{accessor}()" end line_no = __LINE__; str = %{ def #{ali}(*args, &block) begin #{accessor}.__send__(:#{method}, *args, &block) rescue ::Exception [email protected]_if{|s| ::Forwardable::FILE_REGEXP =~ 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 |
#def_instance_delegators(accessor, *methods) ⇒ Object Also known as: def_delegators
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
153 154 155 156 157 158 159 |
# File 'lib/forwardable.rb', line 153 def def_instance_delegators(accessor, *methods) methods.delete("__send__") methods.delete("__id__") for method in methods def_instance_delegator(accessor, method) end end |
#instance_delegate(hash) ⇒ Object Also known as: delegate
Takes a hash as its argument. The key is a symbol or an array of symbols. These symbols correspond to method names. The value is the accessor to which the methods will be delegated.
:call-seq:
delegate method => accessor
delegate [method, method, ...] => accessor
133 134 135 136 137 138 139 140 |
# File 'lib/forwardable.rb', line 133 def instance_delegate(hash) hash.each{ |methods, accessor| methods = [methods] unless methods.respond_to?(:each) methods.each{ |method| def_instance_delegator(accessor, method) } } end |