Rails:猴子修补ActiveRecord :: Base与创建模块

时间:2015-08-21 10:50:19

标签: ruby ruby-on-rails-4 inheritance activerecord callback

我正在阅读The Rails 4 way(作者:Obie Fernandez),这是一本关于Rails的着名书籍,从我到目前为止所读到的内容,我强烈推荐它。

但是,有一个示例部分 9.2.7.1:一个类中的多个回调方法让我困惑:

请耐心等待,为了让每个人都明白这个问题,我已经复制了这本书中描述的步骤。

该部分讨论Active Record回调(before_createbefore_update等),并且可以创建一个为您处理多个回调的类。列出的代码如下:

class Auditor
   def initialize(audit_log)
      @audit_log = audit_log
   end

   def after_create(model)
      @audit_log.created(model.inspect)
   end

   def after_update(model)
      @audit_log.updated(model.inspect)
   end

   def after_destroy(model)
      @audit_log.destroyed(model.inspect)
   end
end

然后该书说将此审核日志记录添加到Active Record类,您将执行以下操作

class Account < ActiveRecord::Base
   after_create Auditor.new(DEFAULT_AUDIT_LOG)
   after_update Auditor.new(DEFAULT_AUDIT_LOG)
   after_destroy Auditor.new(DEFAULT_AUDIT_LOG)
   ...
end

本书随后指出这段代码非常难看,不得不在三行上添加三个审核员,而且它不干。然后它继续告诉我们要解决这个问题,我们应该将acts_as_audited方法修补到Active Record::Base对象中,如下所示:

(该书建议将此文件放在/lib/core_ext/active_record_base.rb

class ActiveRecord::Base
   def self.acts_as_audited(audit_log=DEFAULT_AUDIT_LOG)
      auditor = Auditor.new(audit_log)
      after_create auditor
      after_update auditor
      after_destroy auditor
   end
end

使您可以按如下方式编写Account Model类:

class Account < ActiveRecord::Base
   acts_as_audited
   ...
end

在阅读本书之前,我已经做了类似的事情,为多个Active Record模型添加了功能。我使用的技术是创建一个模块。为了坚持这个例子,我所做的与以下内容类似:

(我会将此文件放在/app/models/auditable.rb

module Auditable
   def self.included(base)
      @audit_log = base.audit_log || DEFAULT_AUDIT_LOG #The base class can override it if wanted, by specifying a self.audit_log before including this module
      base.after_create audit_after_create
      base.after_update audit_after_update
      base.after_destroy audit_after_destroy
   end

   def audit_after_create
      @audit_log.created(self.inspect)
   end

   def audit_after_update
      @audit_log.updated(self.inspect)
   end

   def audit_after_destroy
      @audit_log.destroyed(self.inspect)
   end
end

请注意,此文件都替换了Auditor和猴子修补的ActiveRecord::Base方法。然后Account类看起来像:

class Account < ActiveRecord::Base
   include Auditable
   ...
end

现在你已经读过这本书的方式,以及我过去的方式。我的问题:从长远来看哪个版本更具可持续性?我意识到这是一个有点自以为是的问题,就像关于Rails的一切一样,但为了让它负责,我基本上想知道:< / p>

  • 你为什么要直接修补ActiveRecord::Base,而不是创建并包含Module

1 个答案:

答案 0 :(得分:2)

出于某些原因,我会选择该模块。

显而易见;也就是说,我可以快速找到定义此行为的代码。在acts_as_*我不知道它是来自某些gem,库代码还是在这个类中定义的。它可能会在调用堆栈中被覆盖或捎带。

便于携带。它使用通常在定义回调的库中定义的方法调用。您可以想象在非活动记录对象中分发和使用此库。

它避免在静态级别添加不必要的代码。我喜欢用更少代码来管理(减少要破解的代码)。我喜欢使用Ruby的细节,而没有做太多的事情来迫使它比现在更“好”。

在猴子补丁设置中,您将代码绑定到可能会消失的类或模块,并且在某些情况下它会无声地失败,直到您的班级无法调用acts_as_*

可移植性参数的一个缺点是测试参数。在这种情况下,我会说你可以编写你的代码来保护免受可移植性的影响,或者提前通过智能警告来解决在便携式使用时会发生什么作用和什么不行的问题。