2008-08-14 2 views
34

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

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

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

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

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

ответ

40

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

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 
0

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

class ActiveRecord::Base 
    def after_initialize 
    end 
end 

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

+1

Downvoted, потому что это не является общим решением. Дело в том, что вы не знаете, существует ли супер, так что monkeypatching ActiveRecord :: Base # after_initialize будет существовать, вы создадите точку, в которой ваш код сломается, если/когда ActiveRecord добавляет Base # after_initialize или изменяется его arity ; это далеко не так условно назвать это, если оно определено. – yaauie 2012-04-03 00:15:10

+0

@yaauie - конечно, я мог бы поставить `raise 'oh no' if methods.include? (: After_initialize)` перед monkeypatch, но это сделало бы труднее понять этот пример ... Так легко попасть подробно описывая все случаи кросс, что настоящий урок здесь (просто исправить базовый метод) будет потерян в шуме. – 2012-04-11 00:29:33

3

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

3

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

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

позже:

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

2

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

super if respond_to?('super') 

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

Смежные вопросы