基本上,我注意到在ActiveRecord::Base
钩子内以及在模型本身内部动态覆盖after_initialize
模型的getter时,性能存在很大差异。
说我有以下问题:
module Greeter
extend ActiveSupport::Concern
included do
after_initialize { override_get_greeting }
end
def override_get_greeting
self.class::COLS.each do |attr|
self.class.class_eval do
define_method attr do
"Hello #{self[attr]}!"
end
end
end
end
end
然后我得到以下模型,该模型包含一个带有名称的表。
CREATE TABLE 'names' ( 'name' varchar(10) );
INSERT INTO names (name) VALUES ("John")
class Name < ActiveRecord::Base
COLS = %w("name")
include Greeter
end
john = Name.where(name: 'John').first
john.name # Hello John!
这很好。但是,如果我尝试以更多的 Rails 方式执行此操作,则速度会明显变慢。
本质上,我只想简单地将一个参数传递到包含Greeter
的{{1}}方法中,然后覆盖该getter。看起来像这样:
COLS
现在# Greeter
module Greeter
extend ActiveSupport::Concern
def override_get_greeting(cols)
cols.each do |attr|
self.class.class_eval do
define_method attr do
"Hello #{self[attr]}!"
end
end
end
end
end
# Name
class Name < ActiveRecord::Base
include Greeter
override_get_greeting [:name]
end
的第一次通话时间慢了大约2秒。
我不能把手伸进去。我假设从第一个示例开始,应用程序的运行速度会变慢,但并不确定。
我更喜欢第二个示例,但是性能差异很大。
有人遇到过这样的事情吗?
答案 0 :(得分:1)
除非实际的应用程序代码与上面显示的代码根本不同,否则这绝对不会造成2秒的性能下降!
但是,这仍然是一种不必要的冗长且效率低下的代码编写方式:每次每次初始化类时,您都要在类实例上重新定义方法。
代替使用after_initialize
,您只需定义一次方法即可。例如,您可以将其放入Greeter
模块中:
included do |klass|
klass::COLS.each do |attr|
define_method attr do
"Hello #{self[attr]}!"
end
end
end
还值得注意的是,您可能希望使用self[attr]
而不是super()
。行为将是相同的(假定不存在其他替代),但是如果该列不存在,则会引发错误。