Страницы

Поиск по вопросам

суббота, 4 января 2020 г.

Дублирование методов в модулях в Ruby

#ruby


Нужно добавить модуль, который добавляет объекту массив. Данный код почему-то не
работает, доступа в методах к массиву @comments нет

module Library
  module Commentable
    attr_accessor :comments
    def initialize author, title
      @comments = []
      super author, title
    end
  end
end

class Library::Book
  include Library::Commentable
  attr_accessor :author, :title
  def initialize author, title
    @author = author
    @title = title
  end
end

    


Ответы

Ответ 1



Потому что выходит, что сначала вызывается initialize у Library::Book, который унаследованный метод (из Commentable) не вызывает (super там нет). Library::Book.ancestors #=> [ # Library::Book, <-- он первый по списку # Library::Commentable, # Object, # Kernel, # BasicObject # ] Если забыть о том факте, что "комментируемое" у вас почему-то принимает аргументы, характерные книге (потому что это деталь задачи, о которой вы ничего не рассказали), возможных выходов два: (в Ruby 2.0 и новее) Заменить include на prepend, чтобы модуль оказался впереди всей цепочки поиска методов (разве что после метакласса, но это уже дебри). Перенести вызов super в Library::Book (и вспомнить, что вызов super без аргументов и скобок прокидывает аргументы текущего вызова).

Ответ 2



Добавлю свои пять копеек. В модуле можно перезагрузить метод included, который выполняется каждый раз, когда модуль включается в другой модуль или класс при помощи инструкции include. В качестве аргумента методу передается класс, куда модуль был подмешан, что позволяет определить собственные методы: module Library module Commentable def self.included(base) base.class_eval do def comments= comments @comments = comments end def comments @comments end end end end end class Library::Book include Library::Commentable attr_accessor :author, :title def initialize author, title @author = author @title = title end end o = Library::Book.new 'Мария Эрих Ремарк', 'Три товарища' o.comments = [] p o.comments Однако, как уже упомянул D-side, гораздо более элегантно задачу можно решить при помощи метода prepend, который позволяет решить задачу более элегантно module Library module Commentable attr_accessor :comments def initialize author, title @author = author @title = title @comments = [] end end end class Library::Book prepend Library::Commentable attr_accessor :author, :title def initialize author, title @author = author @title = title end end o = Library::Book.new 'Мария Эрих Ремарк', 'Три товарища' p o.comments p o.author В последнем примере вы затираете метод Library::Book#initialize методом Library::Commentable#initialize. Здесь это смысла не имеет - так как пример очень компактный, но если вам нужно где-то на лету изменить поведение класса, его можно открыть и "поправить" при помощи вашего модуля class Library::Book prepend Library::Commentable end

Комментариев нет:

Отправить комментарий