简体   繁体   English

Rails的辅助方法记忆

[英]Rails Memoization of Helper method

I have a helper method that does expensive calculations and returns a Hash , and this Hash is constant during my entire application lifespan (meaning it can only change after a re-deploy) and it also doesn't take any arguments. 我有一个辅助方法,该方法执行昂贵的计算并返回Hash ,并且该Hash在我的整个应用程序生命周期中都是恒定的(这意味着它只能在重新部署后才能更改),并且也不接受任何参数。

For performance, I wish I could 'cache' the resulting Hash . 为了提高性能,我希望可以“缓存”生成的Hash

I don't want to use Rails cache for this, since I want to avoid the extra trip to memcached and I don't want the overhead of de-serializing the string into a hash. 不想用Rails缓存对于这一点,因为我想避免额外的行程Memcached和我不想要的反序列串入一个散列的开销。

My first idea was to assign the resulting hash to a Constant and calling .freeze on it. 我的第一个想法是将结果哈希分配给Constant并在其上调用.freeze But the helper is an instance method, the constant lives on the class, and I had to do this ultra hacky solution: 但是帮助程序是一个实例方法,常量始终存在于类中,我不得不执行以下超级hacky解决方案:

module FooHelper

def expensive_calculation_method
  resulting_hash
end

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

This is due to the helper method being an instance method, the helper being a Module (which leads to the fake Class extend so I can call the instance method) and I also must declare the constant AFTER the instance method (if I declare it right after module FooHelper , I get an undefined method 'expensive_calculation_method' . 这是由于helper方法是实例方法,helper是Module(导致伪类扩展,所以我可以调用实例方法),并且我还必须在实例方法之后声明常量AFTER(如果我正确声明的话)在module FooHelper ,我得到了一个undefined method 'expensive_calculation_method'

The second idea was to use memoization, but at least for Rails Controllers memoization is the persistance of a variable over the lifecycle of a single request , so it's only valuable if you reuse a variable many times from within a single request , which is not my case, but at the same time Helpers are modules, not Classes to be instanciated, and by this point I don't know what to do. 第二个想法是使用记忆,但是至少对于Rails Controller来说,记忆是在单个请求的生命周期内变量的持久性,因此仅当您在单个请求中多次重复使用变量时才有价值,这不是我的想法。情况,但同时Helpers是模块,而不是要实例化的类,并且到目前为止,我不知道该怎么做。

How would I cache that Hash, or memoize it in a way that persists over requests? 我将如何缓存该哈希,或以对请求的持久化方式对其进行记忆?

If you want to cache the result of some painful operation at launch time: 如果要在启动时缓存某些痛苦的操作的结果,请执行以下操作:

module MyExpensiveOperation
  COMPUTED_RESULT = OtherModule.expensive_operation

  def self.cached
    COMPUTED_RESULT
  end
end

Just make sure that module's loaded somehow or it won't initiate. 只需确保以某种方式加载了该模块,否则该模块将不会启动。 You can always force- require that module if necessary in environment.rb or as a config/initializer type file. 您始终可以在environment.rb或作为config/initializer类型文件强制require该模块。

If you want to lazy load the basic principle is the same: 如果要延迟加载,其基本原理是相同的:

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

    @cached = OtherModule.expensive_operation
  end
end

That will handle operations that, for whatever reason, return nil or false . 这将处理由于任何原因返回nilfalse It will run once, and once only, unless you have multiple threads triggering it at the same time. 它将运行一次,并且只能运行一次,除非您有多个线程同时触发它。 If that's the case there's ways of making your module concurrency aware with automatic locks. 如果是这种情况,则可以通过自动锁定来使您的模块并发感知。

Per your comments, this will only change at application boot, so placing it in an initializer would do the trick. 根据您的评论,这只会在应用程序启动时发生变化,因此将其放在初始化程序中就可以解决问题。

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

# or
EXPENSIVE_THING_CONSTANT = expensive_calculation

# or

Rails.application.config.expensive_thing = expensive_calcualatioin

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM