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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top