Spec-Zone.ru › Ruby 2.2

Модуль Forwardable

Модуль Forwardable предоставляет делегирование указанных методов к назначенному объекту, используя методы def_delegator и def_delegators.

Например, предположим, что у вас есть класс RecordCollection, который содержит массив @records. Вы можете предоставить метод поиска record_number(), который просто вызывает [] на @records массиве, так:

require 'forwardable'

class RecordCollection
  attr_accessor :records
  extend Forwardable
  def_delegator :@records, :[], :record_number
end

Мы можем использовать метод поиска следующим образом:

r = RecordCollection.new
r.records = [4,5,6]
r.record_number(0)  # => 4

Далее, если вы хотите предоставить методы size, <

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]

Вы даже можете расширить обычные объекты с помощью 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!"

Другой пример

Мы хотим полагаться на то, что было прежде, очевидно, но с делегированием мы можем взять только те методы, которые нам нужны, и даже переименовать их должным образом. Во многих случаях это предпочтительнее наследования, которое предоставляет нам весь старый интерфейс, даже если большая часть из него не нужна.

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

Это должно вывести:

2
3
4
5
6
Ruby
nil

Примечания

Обратите внимание, что RDoc не будет обнаруживать делегированные методы.

forwardable.rb предоставляет делегирование по одному методу через методы #def_delegator и #def_delegators. Для делегирования всего класса с помощью DelegateClass см. delegate.rb.

Константы

FILE_REGEXP
FORWARDABLE_VERSION

Версия forwardable.rb

Атрибуты

debug[RW]

Если true, __FILE__ останется в стеке вызовов в случае возникновения Исключения.

Публичные методы класса

_delegator_method(obj, accessor, method, ali) Показать исходный код
# File lib/forwardable.rb, line 191
def self._delegator_method(obj, accessor, method, ali)
  accessor = accessor.to_s unless Symbol === accessor

  if Module === obj ?
       obj.method_defined?(accessor) || obj.private_method_defined?(accessor) :
       obj.respond_to?(accessor, true)
    accessor = "#{accessor}()"
  end

  line_no = __LINE__+1; str = "#{<<-"begin;"}\n#{<<-"end;"}"
  begin;
    proc do
      def #{ali}(*args, &block)
        begin
          #{accessor}
        ensure
          $@.delete_if {|s| ::Forwardable::FILE_REGEXP =~ s} if $@ and !::Forwardable::debug
        end..__send__ #{method}, *args, &block
      end
    end
  end

Публичные методы экземпляра

def_delegator(accessor, method, ali = method)
Псевдоним для: def_instance_delegator
def_delegators(accessor, *methods)
Псевдоним для: def_instance_delegators
def_instance_delegator(accessor, method, ali = method) Показать исходный код
# File lib/forwardable.rb, line 180
def def_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 instance
  (Module === self ? self : singleton_class).module_eval(&gen)
end

Определите method как метод делегата экземпляра с необязательным именем псевдонима ali. Вызовы метода ali будут делегированы 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
Также псевдоним: def_delegator
def_instance_delegators(accessor, *methods) Показать исходный код
# 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

Сокращение для определения нескольких методов делегата, но без возможности использовать другое имя. Два следующих примера кода имеют одинаковый эффект:

def_delegators :@records, :size, :<<, :map

def_delegator :@records, :size
def_delegator :@records, :<<
def_delegator :@records, :map
Также псевдоним: def_delegators
delegate(hash)
Псевдоним для: instance_delegate
delegate method → accessor Показать исходный код
delegate [method, method, ...] → accessor
# 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

Принимает хэш в качестве аргумента. Ключ — символ или массив символов. Эти символы соответствуют именам методов. Значение — обработчик, к которому будут делегированы методы.

Также псевдоним: delegate

Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API