Ruby mixins和调用超级方法

时间:2020-03-05 18:39:34  来源:igfitidea点击:

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

到目前为止,一切都很好。看起来它将解决,但是我遇到了一个不确定如何解决的问题。该模块(我称为sendable)将成为处理传真,电子邮件或者打印文档PDF的代码。例如,我有一个采购订单,而我有一个内部销售订单(想象中缩写为ISO)。

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

我的问题是,我的任何一个mixin中都可以有一个after_initialize,因此我需要在开始时包括一个超级调用,以确保其他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

因此,如果每个混入都具有一个after_initialize调用以及一个超级调用,那么如何停止最后一个超级调用引发错误呢?在调用超级方法之前,如何测试超级方法是否存在?

解决方案

回答

我们可以只定义它而不是检查super方法是否存在。

class ActiveRecord::Base
    def after_initialize
    end
end

这在我的测试中有效,并且不应该破坏我们现有的任何代码,因为所有其他定义该代码的类都将以静默方式重写此方法。

回答

我们是否尝试过" alias_method_chain"?我们基本上可以将所有的after_initialize调用链接起来。它就像一个装饰器:每个新方法都添加了新的功能层,并将控件传递给"覆盖"方法以完成其余工作。

回答

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

Alias_method_chain`还具有创建after_initialize方法的命名版本的优点,这意味着我们可以在重要的罕见情况下自定义呼叫顺序。否则,我们将受到包含类包含混合对象的任何命令的支配。

之后:

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

回答

我们可以在其中快速添加条件:

super if respond_to?('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