Рубиновые миксины и вызов супер-методов - PullRequest
34 голосов
/ 14 августа 2008

Хорошо, я реорганизовал свой код в своем маленьком приложении на Rails, пытаясь убрать дублирование и в целом сделать мою жизнь проще (а мне нравится легкая жизнь). Часть этого рефакторинга состояла в том, чтобы переместить код, который является общим для двух моих моделей, в модуль, который я могу включить туда, где он мне нужен.

Пока все хорошо. Похоже, это сработает, но я столкнулся с проблемой, которую я не знаю, как обойти. Модуль (который я назвал sendable) будет просто кодом, который обрабатывает факсы, электронную почту или печать PDF документа. Так, например, у меня есть заказ на покупку, и у меня есть Внутренние заказы на продажу (образно сокращенно ISO).

Проблема, с которой я столкнулся, заключается в том, что я хочу инициализировать некоторые переменные (инициализированные для людей, которые пишут неправильно: P) после загрузки объекта, поэтому я использовал after_initialize крюк. Нет проблем ... пока я не начну добавлять еще несколько миксинов.

Проблема, с которой я сталкиваюсь, заключается в том, что у меня может быть after_initialize в любом из моих миксинов, поэтому мне нужно включить вызов super в начале, чтобы убедиться, другие вызовы mixin after_initialize. И это здорово, пока я не вызову супер, и не получу сообщение об ошибке, потому что звонить в супер нет.

Вот небольшой пример, если я не слишком запутался:

class Iso < ActiveRecord::Base
  include Shared::TracksSerialNumberExtension
  include Shared::OrderLines
  extend  Shared::Filtered
  include Sendable::Model

  validates_presence_of   :customer
  validates_associated    :lines

  owned_by                :customer
  order_lines             :despatched # Mixin

  tracks_serial_numbers   :items  # Mixin

  sendable :customer                      # Mixin

  attr_accessor :address

  def initialize( params = nil )
    super
    self.created_at ||= Time.now.to_date
  end
end

Итак, если у каждого из миксов есть вызов after_initialize с вызовом super , как я могу остановить этот последний вызов super , чтобы вызвать ошибку? Как я могу проверить, что супер метод существует, прежде чем я его вызову?

Ответы [ 5 ]

41 голосов
/ 06 октября 2010

Вы можете использовать это:

super if defined?(super)

Вот пример:

class A
end

class B < A
  def t
    super if defined?(super)
    puts "Hi from B"
  end
end

B.new.t
3 голосов
/ 15 августа 2008

Вы пробовали alias_method_chain? Вы можете, в основном, приковать цепочкой все свои after_initialize звонки. Он действует как декоратор: каждый новый метод добавляет новый уровень функциональности и передает управление «переопределенному» методу, чтобы сделать все остальное.

3 голосов
/ 15 августа 2008

Включающий класс (вещь, которая наследуется от ActiveRecord::Base, который в данном случае Iso) может определять свой собственный after_initialize, поэтому любое решение, кроме alias_method_chain ( другой псевдоним, сохраняющий оригинал) рискует перезаписать код. Решение @Orion Edwards - лучшее, что я могу придумать. Есть и другие, но они далеко более хакерские.

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

позже

Я отправил вопрос в список рассылки ruby-on-rails-core о создании по умолчанию пустых реализаций всех обратных вызовов. Процесс сохранения в любом случае проверяет их все, поэтому я не понимаю, почему их там не должно быть. Единственным недостатком является создание дополнительных пустых кадров стека, но это довольно дешево для каждой известной реализации.

2 голосов
/ 25 мая 2009

Вы можете просто добавить туда быстрое условие:

super if respond_to?('super')

и у вас все будет хорошо - не добавляйте бесполезных методов; красиво и чисто.

0 голосов
/ 15 августа 2008

Вместо того, чтобы проверять, существует ли супер метод, вы можете просто определить его

class ActiveRecord::Base
    def after_initialize
    end
end

Это работает в моем тестировании и не должно нарушать какой-либо из ваших существующих кодов, потому что все другие ваши классы, которые его определяют, будут просто переопределять этот метод в любом случае

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...