Rails Memoization of Helper方法

时间:2017-11-02 18:01:45

标签: ruby-on-rails ruby

我有一个辅助方法,它执行昂贵的计算并返回Hash,并且此Hash在我的整个应用程序生命周期内是不变的(意味着它只能在重新部署后更改)并且它也不会任何争论。

为了提高性能,我希望我可以“缓存”生成的Hash

想要为此使用Rails缓存,因为我想避免额外的memcached之旅,我不希望将字符串反序列化为哈希的开销。

我的第一个想法是将结果哈希值分配给常量并在其上调用.freeze。但帮助器是一个实例方法,常量存在于类中,我不得不做这个超级hacky解决方案:

module FooHelper

def expensive_calculation_method
  resulting_hash
end

EXPENSIVE_CALCULATION_CONSTANT = Class.new.extend(self).expensive_calculation_method.freeze

这是因为辅助方法是一个实例方法,帮助程序是一个模块(导致假类扩展所以我可以调用实例方法)而且我还必须在实例方法之后声明常量(如果我在module FooHelper之后立即声明,我得到undefined method 'expensive_calculation_method'

第二个想法是使用memoization,但至少对于Rails控制器来说,memoization是变量在单个请求的生命周期中的持久性,因此只有在多次重用变量时它才有用。来自单一请求,这不是我的情况,但同时Helpers是模块,而不是要实例化的类,此时我不知道该怎么做。

我如何缓存哈希值,或以持续超过请求的方式记忆它?

2 个答案:

答案 0 :(得分:0)

如果你想在发布时缓存一些痛苦的操作结果:

module MyExpensiveOperation
  COMPUTED_RESULT = OtherModule.expensive_operation

  def self.cached
    COMPUTED_RESULT
  end
end

确保以某种方式加载模块或不启动模块。您可以随时在requireenvironment.rb类型文件中强制使用config/initializer该模块。

如果你想延迟加载,基本原理是一样的:

module MyExpensiveOperation
  def self.cached
    return @cached if (defined?(@cached))

    @cached = OtherModule.expensive_operation
  end
end

这将处理因任何原因返回nilfalse的操作。它将运行一次,只运行一次,除非您有多个线程同时触发它。如果是这种情况,可以通过自动锁定方式识别模块的并发性。

答案 1 :(得分:0)

根据您的评论,这只会在应用程序启动时更改,因此将其放在初始化程序中就可以了。

# config/initializers/expensive_thing.rb
$EXENSIVE_THING_GLOBAL = expensive_calculation

# or
EXPENSIVE_THING_CONSTANT = expensive_calculation

# or

Rails.application.config.expensive_thing = expensive_calcualatioin