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

Qqw*_*qwy 11 ruby inheritance activerecord callback ruby-on-rails-4

我正在阅读The Rails 4的方式(由Obie Fernandez撰写),一本关于Rails的着名书籍,从我到目前为止所阅读的内容,我强烈推荐它.

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

忍受我,为了让每个人都明白这个问题,我已经复制了本书在这个问题中描述的步骤.


关于活动记录的回调(本节谈判before_create,before_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
Run Code Online (Sandbox Code Playgroud)

该书说,要将此审核日志记录添加到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
Run Code Online (Sandbox Code Playgroud)

然后,本书指出这段代码非常难看,不得不在三行上添加三个审核员,而且它不干.然后继续告诉我们,要解决这个问题,我们应该将一个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
Run Code Online (Sandbox Code Playgroud)

这使您可以编写Account Model类,如下所示:

class Account < ActiveRecord::Base
   acts_as_audited
   ...
end
Run Code Online (Sandbox Code Playgroud)

在阅读本书之前,我已经做了类似的事情,为多个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
Run Code Online (Sandbox Code Playgroud)

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

class Account < ActiveRecord::Base
   include Auditable
   ...
end
Run Code Online (Sandbox Code Playgroud)

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

  • 你为什么要ActiveRecord::Base直接修补猴子,而不是创造和包括Module

Jon*_*now 2

我会选择这个模块有几个原因。

很明显; 也就是说,我可以很快找到定义这个行为的代码。我acts_as_*不知道它是否来自某些 gem、库代码或在此类中定义。它可能会被覆盖或搭载在调用堆栈中。

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

它避免了在静态级别添加不必要的代码。我喜欢管理更少的代码(需要破坏的代码更少)。我喜欢使用 Ruby 的优点,而不需要做太多事情来强迫它比现在“更好”。

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

可移植性论证的失败之一是测试论证。在这种情况下,我想说,您可以编写代码来防止可移植性,或者尽早失败,并发出智能警告,说明在可移植性使用时什么会起作用,什么不会起作用。