关注问题方法的性能差异是挂钩还是模型?

时间:2019-05-13 15:25:36

标签: mysql ruby-on-rails

基本上,我注意到在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秒。

我不能把手伸进去。我假设从第一个示例开始,应用程序的运行速度会变慢,但并不确定。

我更喜欢第二个示例,但是性能差异很大。

有人遇到过这样的事情吗?

1 个答案:

答案 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()。行为将是相同的(假定不存在其他替代),但是如果该列不存在,则会引发错误。