【发布时间】: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。
这是实验的结果:
- 对于我没有覆盖的属性,
- This line 被调用
- 然后方法在this module eval call里面定义
- 最后,newly created writer method 在执行
object.attribute=时被正确调用
- 对于我 DID 覆盖的属性,
- 我自己的方法是在其他任何东西之前定义的(当 ActiveRecord 编写器还不存在时
- 然后 ActiveRecord 调用 the same line 来处理编写器的创建,如上例所示
- 该方法(显然)由 ActiveRecord 正确创建,因为它再次通过 by this point
- 但现在,令人惊讶的是,当调用
object.attribute=时,我自己的方法仍然被调用来代替 ActiveRecord 方法
所以,我不明白的是:如果 ActiveRecord 似乎覆盖了我的方法,但它没有,是什么阻止了它?
我的问题
最后我需要知道的是,我所做的修复是否实际上是一个好的做法(并且是可靠的),或者它是否存在风险,如果将来我们进行升级,它可能会中断。
如果您认为我的解决方法很危险,您能否提出不同的方法来实现相同的目标?
【问题讨论】:
标签: activerecord overriding ruby-on-rails-5 rails-activerecord