Ruby混合和调用超级方法

好吧,所以我一直在我的小Rails应用程序中重构我的代码,以消除重复,一般来说让我的生活更轻松(因为我喜欢简单的生活)。 这个重构的一部分,一直是将我的两个模型中通用的代码移动到一个模块,我可以将其包含在我需要的地方。

到现在为止还挺好。 看起来它会运行,但我刚刚遇到一个问题,我不知道如何解决。 该模块(我称之为“可发送”)只是处理传真,电子邮件或打印文档PDF的代码。 所以,例如,我有一个采购订单,我有内部销售订单(想象力缩写为ISO)。

我碰到的问题是,我想在加载对象后初始化一些variables(对于拼写不正确的人初始化:P),所以我一直在使用after_initialize钩子。 没问题,直到我开始添加一些mixin。

我所after_initialize的问题是,我可以在任何一个after_initialize中都有一个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调用,并且有一个超级调用,那么我怎样才能阻止最后一次超级调用? 在我打电话之前,如何testing超级方法的存在?

你可以使用这个:

 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调用。 它像一个装饰器:每个新的方法增加了一个新的function层,并将控制权交给“重写”方法来完成剩下的工作。

包含类(从ActiveRecord::Baseinheritance的东西,在这种情况下是Iso可以定义自己的after_initialize ,因此alias_method_chain (或其他保存原始文件的别名)的解决scheme有可能会覆盖代码。 @Orion Edwards的解决scheme是我能想到的最好的解决scheme。 还有其他的,但他们更加hackish。

alias_method_chain还具有创buildafter_initialize方法的命名版本的好处,这意味着您可以在极less数情况下自定义调用顺序。 否则,你不受任何包含混合类的命令的束缚。

后来

我已经向ruby-on-rails-core邮件列表发布了关于创build所有callback的默认空实现的问题。 无论如何,保存过程会检查它们,所以我不明白为什么他们不应该在那里。 唯一的缺点是创build额外的空栈帧,但是在每个已知的实现上都相当便宜。

你可以在这里抛出一个快速的条件:

 super if respond_to?('super') 

你应该没问题 – 不要添加无用的方法; 不错,干净。

而不是检查超级方法是否存在,你可以定义它

 class ActiveRecord::Base def after_initialize end end 

这在我的testing中起作用,不应该破坏你现有的任何代码,因为所有其他定义它的类都会默默地覆盖这个方法