Ruby mixins和调用超级方法

时间:2008-08-14 08:30:28

标签: ruby-on-rails ruby

好的,所以我一直在我的小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方法?

5 个答案:

答案 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

这适用于我的测试,不应该破坏你现有的任何代码,因为所有其他定义它的类都会默默地覆盖这个方法