Ответ 1
Вы можете использовать это:
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
Хорошо, поэтому я реорганизовал свой код в своем маленьком приложении 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 из-за повышения ошибки? Как я могу проверить, существует ли метод супер до того, как я его назову?
Вы можете использовать это:
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
Вы пробовали alias_method_chain
? Вы можете в основном связать все ваши звонки after_initialize
. Он действует как декоратор: каждый новый метод добавляет новый уровень функциональности и передает управление на "переопределенный" метод, чтобы сделать все остальное.
Входящий класс (вещь, которая наследует от ActiveRecord::Base
, которая в этом случае Iso
) может определить свой собственный after_initialize
, поэтому любое решение, отличное от alias_method_chain
(или другое наложение, которое сохраняет исходный текст ) рискует переписать код. Решение @Orion Edwards - лучшее, что я могу придумать. Есть и другие, но они далеко более хаки.
alias_method_chain
также имеет преимущество создания именованных версий метода after_initialize, то есть вы можете настроить порядок вызовов в тех редких случаях, которые имеют значение. В противном случае вы будете во власти любого порядка включительного класса включите mixins.
позже:
Я разместил вопрос в списке рассылки ruby-on-rails о создании стандартных пустых реализаций всех обратных вызовов. Процесс сохранения все равно проверяет их, поэтому я не понимаю, почему их не должно быть. Единственным недостатком является создание дополнительных пустых кадров стека, но это довольно дешево для каждой известной реализации.
Вы можете просто бросить быструю условность:
super if respond_to?('super')
и вы должны быть в порядке - не добавлять бесполезные методы; приятный и чистый.
Вместо того, чтобы проверять, существует ли супер-метод, вы можете просто определить его
class ActiveRecord::Base
def after_initialize
end
end
Это работает в моем тестировании и не должно разорвать какой-либо из вашего существующего кода, потому что все ваши другие классы, которые его определяют, просто будут молча отменять этот метод в любом случае