好的,所以我一直在我的小Rails应用程序中重构我的代码以努力消除重复,并且通常会让我的生活更轻松(因为我喜欢简单的生活)。这个重构的一部分,就是将我的两个模型共同的代码移动到我可以包含的模块中。
到目前为止,这么好。看起来它会成功,但我遇到了一个我不确定如何解决的问题。该模块(我称之为可发送模块)将成为处理传真,电子邮件或打印文档PDF的代码。所以,例如,我有一个采购订单,我有内部销售订单(想象地缩写为ISO)。
我遇到的问题是,我想要在加载对象后初始化一些变量(初始化为没有拼写正确的人:P),所以我一直在使用 after_initialize 钩子。没问题......直到我开始添加更多mixins。
我遇到的问题是,我的任何一个mixin都可以有 after_initialize
,所以我需要在超级调用开始确保调用其他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
因此,如果每个mixin都有一个after_initialize调用,并且通过超级调用,如何阻止最后一次超级调用引发错误?在调用super方法之前,如何测试super方法?
答案 0 :(得分:41)
您可以使用:
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
答案 1 :(得分:3)
你试过alias_method_chain
吗?您基本上可以将所有after_initialize
电话联系起来。它就像一个装饰器:每个新方法都添加了一个新的功能层,并将控件传递给“重写”方法来完成剩下的工作。
答案 2 :(得分:3)
包含类(继承自ActiveRecord::Base
的东西,在本例中为Iso
)可以定义自己的after_initialize
,因此任何解决方案除了alias_method_chain
(或其他保存原文的别名)之外,还有覆盖代码的风险。 @Orion Edwards的解决方案是我能想到的最好的解决方案。还有其他人,但他们远更加hackish。
alias_method_chain
还具有创建after_initialize方法的命名版本的好处,这意味着您可以在极少数情况下自定义调用顺序。否则,你将受到包含类包含mixins的任何顺序的支配。
后:
我在ruby-on-rails-core邮件列表上发布了一个关于创建所有回调的默认空实现的问题。保存过程无论如何都要检查它们,所以我不明白为什么它们不应该在那里。唯一的缺点是创建额外的空堆栈帧,但这在每个已知的实现上都相当便宜。
答案 3 :(得分:2)
你可以在那里快速提出条件:
super if respond_to?('super')
你应该没事 - 没有添加无用的方法;很干净。
答案 4 :(得分:0)
不是检查超级方法是否存在,而是可以定义它
class ActiveRecord::Base
def after_initialize
end
end
这适用于我的测试,不应该破坏你现有的任何代码,因为所有其他定义它的类都会默默地覆盖这个方法