35

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

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

我遇到的问题是,我希望在加载对象后初始化一些变量(为拼写不正确的人初始化 :P ),所以我一直在使用after_initialize钩子。没问题......直到我开始添加更多的mixin。

我遇到的问题是,我可以在我的after_initialize任何一个 mixin 中都有一个,所以我需要在开始时包含一个super调用以确保调用其他 mixinafter_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

那么,如果每个 mixin 都有一个 after_initialize 调用,以及一个super调用,我怎样才能阻止最后一个super调用引发错误?如何在调用之前测试超级方法是否存在?

4

5 回答 5

42

你可以使用这个:

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
于 2010-10-05T20:34:17.547 回答
3

你试过alias_method_chain吗?您基本上可以将所有after_initialize电话链接起来。它就像一个装饰器:每个新方法都会添加一个新的功能层,并将控制权传递给“覆盖”的方法来完成其余的工作。

于 2008-08-14T22:37:56.463 回答
3

包含类(从 继承的东西ActiveRecord::Base,在这种情况下是Iso可以定义自己的,因此除了(或保存原始的其他别名)之外的after_initialize任何解决方案都有覆盖代码的风险。alias_method_chain@Orion Edwards 的解决方案是我能想到的最好的解决方案。还有其他的,但它们骇人听闻。

alias_method_chain还具有创建 after_initialize 方法的命名版本的好处,这意味着您可以在重要的罕见情况下自定义调用顺序。否则,您将受包含类包含混入的任何顺序的支配。

后来

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

于 2008-08-15T01:28:04.920 回答
2

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

super if respond_to?('super')

你应该没问题 - 不要添加无用的方法;干净整洁。

于 2009-05-25T06:53:52.397 回答
0

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

class ActiveRecord::Base
    def after_initialize
    end
end

这在我的测试中有效,并且不应破坏您现有的任何代码,因为定义它的所有其他类无论如何都会默默地覆盖此方法

于 2008-08-14T21:30:58.813 回答