【问题标题】:ActiveRecord: override attribute writers by using a class methodActiveRecord:使用类方法覆盖属性编写器
【发布时间】:2018-04-10 17:46:29
【问题描述】:

我不知道如何正确表达标题,我认为解释这个问题的最好方法就是使用代码示例。

我的目标

我想定义这样的元方法(在 Rails 5 中):

class Post < ApplicationRecord
  override_this_attribute_writer :some_attribute
end

override_this_attribute_writer 遵循一个通用模式,它通过在其之上进行一些过滤来覆盖原始作者。我发现这种覆盖方式非常方便和清晰。

第一种方法

module MyCommonModule
  extend ActiveSupport::Concern

  module ClassMethods
    def override_this_attribute_writer(attribute_name)
    alias_method :"#{attribute_name}_old=", :"#{attribute_name}="
    define_method :"#{attribute_name}=" do |a_value|
      # Do my stuff
      send(:"#{attribute_name}_old=", a_value)
    end
  end
end

执行此操作时,我在调用 alias_method 时遇到异常,因为显然我试图复制的方法不存在(尚)。

第二种方法

module MyCommonModule
  extend ActiveSupport::Concern

  module ClassMethods
    def override_this_attribute_writer(attribute_name)
    define_method :"#{attribute_name}=" do |a_value|
      # Do my stuff
      send(:write_attribute, attribute_name, a_value)
    end
  end
end

我原以为这不起作用:如果在运行元方法时,ActiveRecord 尚未创建属性编写器,这意味着它将稍后执行并覆盖我刚刚定义的方法。

但令人惊讶的是它起作用了!所以我把手伸进了 ActiveRecord (5.1.5) 以了解更多信息。

深入研究 ActiveRecord 5.1.5

我想确保我所做的事情是安全的,而不是偶然发生的:我查看了the definition of method writer,并在方法周围加上了binding.pry

这是实验的结果:

  • 对于我没有覆盖的属性
    1. This line 被调用
    2. 然后方法在this module eval call里面定义
    3. 最后,newly created writer method 在执行object.attribute= 时被正确调用
  • 对于我 DID 覆盖的属性
    1. 我自己的方法是在其他任何东西之前定义的(当 ActiveRecord 编写器还不存在时
    2. 然后 ActiveRecord 调用 the same line 来处理编写器的创建,如上例所示
    3. 该方法(显然)由 ActiveRecord 正确创建,因为它再次通过 by this point
    4. 但现在,令人惊讶的是,当调用 object.attribute= 时,我自己的方法仍然被调用来代替 ActiveRecord 方法

所以,我不明白的是:如果 ActiveRecord 似乎覆盖了我的方法,但它没有,是什么阻止了它?

我的问题

最后我需要知道的是,我所做的修复是否实际上是一个好的做法(并且是可靠的),或者它是否存在风险,如果将来我们进行升级,它可能会中断。

如果您认为我的解决方法很危险,您能否提出不同的方法来实现相同的目标?

【问题讨论】:

    标签: activerecord overriding ruby-on-rails-5 rails-activerecord


    【解决方案1】:

    调用super 更加地道:

    module MyCommonModule
      extend ActiveSupport::Concern
    
      module ClassMethods
        def override_this_attribute_writer(attribute_name)
          define_method :"#{attribute_name}=" do |value|
            # do some stuff
            super value
          end
        end
      end
    end
    

    【讨论】:

      猜你喜欢
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 2019-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多