Рубиновые миксины и вызов супер-методов

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

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

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

Проблема, с которой я столкнулся, заключается в том, что у меня может быть after_initializeлюбой из моих миксинов, поэтому мне нужно включить супер- вызов в начале, чтобы убедиться, что другие 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 с супер- вызовом, как я могу остановить этот последний супер- вызов, чтобы вызвать ошибку? Как я могу проверить, что супер метод существует, прежде чем я его вызову?

14.08.2008 08:30:27
5 ОТВЕТОВ
РЕШЕНИЕ

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

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
41
5.10.2010 20:34:17

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

class ActiveRecord::Base
    def after_initialize
    end
end

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

0
14.08.2008 21:30:58
Проголосовал, потому что это не общее решение. Дело в том, что вы не знаете, существует или нет супер, поэтому монтируя ActiveRecord :: Base # after_initialize на существование, вы создаете точку, где ваш код сломается, если / когда ActiveRecord добавит Base # after_initialize, или его арность будет изменена ; гораздо лучше условно назвать его, если он определен.
yaauie 3.04.2012 00:15:10
@yaauie - конечно, я мог бы поставить raise 'oh no' if methods.include?(:after_initialize)перед monkeypatch, но это усложнило бы понимание примера ... Так легко разобраться в деталях всех крайних случаев, что настоящий урок здесь (просто исправьте базовый метод) в) потерялся бы в шуме.
Orion Edwards 11.04.2012 00:29:33
monkeypatching не является общим решением, и его, как правило, не следует поощрять. Ответ, который включает одноразовый monekypatch, который может сработать, приводит к излишне сложному и хрупкому коду, особенно если вы не имеете полного контроля над цепочкой наследования.
yaauie 5.07.2012 23:15:46

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

3
15.08.2008 00:56:38

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

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

позже :

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

3
15.08.2008 01:42:12

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

super if respond_to?('super')

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

2
25.05.2009 06:53:52
Это не похоже на работу. response_to? ('super') всегда будет возвращать false.
averell 15.07.2010 11:53:18