ActiveRecord:使用类方法覆盖属性编写器

时间:2018-04-10 17:46:29

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

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

我的目标

我想定义一个像这样的元方法(在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. 中定义该方法
    4. 最后,执行object.attribute=
    5. 时正确调用newly created writer method
  • 对于我强行覆盖的属性
    1. 我自己的方法在之前定义 (当ActiveRecord编写者还不存在时
    2. 然后ActiveRecord调用处理作者创建的the same line,如上例
    3. 该方法(显然)由ActiveRecord正确创建,因为它再次传递by this point
    4. 但是现在,令人惊讶的是,在调用object.attribute=时,仍然会调用我自己的方法代替ActiveRecord

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

我的问题

最后我需要知道的是,我所做的修复实际上是一个好的做法(并且是强大的),还是存在风险,如果将来我们进行升级,它可能会破坏。

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

1 个答案:

答案 0 :(得分:2)

调用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