题
好的,所以我一直在我的小 Rails 应用程序中重构我的代码,以消除重复,总体上让我的生活更轻松(因为我喜欢轻松的生活)。重构的一部分是将我的两个模型通用的代码移动到一个模块中,我可以将其包含在需要的地方。
到目前为止,一切都很好。看起来它会解决问题,但我刚刚遇到了一个我不知道如何解决的问题。该模块(我称之为可发送)只是处理传真、电子邮件或打印 PDF 文档的代码。例如,我有一个采购订单,还有一个内部销售订单(想象中缩写为 ISO)。
我遇到的问题是,我希望在加载对象后初始化一些变量(为拼写错误的人初始化:P),所以我一直在使用 初始化后 钩。没问题...直到我开始添加更多 mixin。
我遇到的问题是我可以有一个 after_initialize
在我的任何一个 mixins 中,所以我需要包含一个 极好的 在开始时调用以确保其他 mixin after_initialize
电话被呼叫。这很棒,直到我最终调用 super 并收到错误,因为没有 super 可以调用。
这是一个小例子,以防我还不够混乱:
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 调用,并带有 极好的 打电话,我怎样才能停止最后的通话 极好的 调用引发错误?在调用 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 方法的命名版本的好处,这意味着您可以在极少数重要的情况下自定义调用顺序。否则,您将受到包含类包含 mixin 的任何顺序的影响。
之后:
我已经向 ruby-on-rails-core 邮件列表发布了一个关于创建所有回调的默认空实现的问题。无论如何,保存过程都会检查它们,所以我不明白为什么它们不应该在那里。唯一的缺点是创建额外的空堆栈帧,但这对于每个已知的实现来说都是相当便宜的。
您可以在其中添加一个快速条件:
super if respond_to?('super')
你应该没问题——不要添加无用的方法;漂亮又干净。
您可以定义它,而不是检查 super 方法是否存在
class ActiveRecord::Base
def after_initialize
end
end
这在我的测试中有效,并且不应该破坏任何现有代码,因为定义它的所有其他类无论如何都会默默地覆盖此方法