Ruby on rails 辅助方法的Rails实现
我有一个helper方法,它执行昂贵的计算并返回一个Ruby on rails 辅助方法的Rails实现,ruby-on-rails,ruby,Ruby On Rails,Ruby,我有一个helper方法,它执行昂贵的计算并返回一个哈希值,这个哈希值在我的整个应用程序生命周期内是不变的(这意味着它只能在重新部署后更改),而且它也不接受任何参数 为了提高性能,我希望能够“缓存”生成的Hash 我不想为此使用Rails缓存,因为我想避免对memcached的额外访问,也不想将字符串反序列化为哈希的开销 我的第一个想法是将得到的散列分配给一个常量,并对其调用.freeze。但是helper是一个实例方法,常量存在于类中,我不得不做这个非常粗糙的解决方案: module FooH
哈希值
,这个哈希值在我的整个应用程序生命周期内是不变的(这意味着它只能在重新部署后更改),而且它也不接受任何参数
为了提高性能,我希望能够“缓存”生成的Hash
我不想为此使用Rails缓存,因为我想避免对memcached的额外访问,也不想将字符串反序列化为哈希的开销
我的第一个想法是将得到的散列分配给一个常量,并对其调用.freeze
。但是helper是一个实例方法,常量存在于类中,我不得不做这个非常粗糙的解决方案:
module FooHelper
def expensive_calculation_method
resulting_hash
end
EXPENSIVE_CALCULATION_CONSTANT = Class.new.extend(self).expensive_calculation_method.freeze
这是因为helper方法是一个实例方法,helper是一个模块(这导致了伪类extend,因此我可以调用实例方法),并且我还必须在实例方法之后声明常量(如果我在模块fooheloper
之后立即声明它,我会得到一个未定义的方法“昂贵的计算方法”
第二个想法是使用记忆,但至少对于Rails控制器来说,记忆是一个变量在单个请求的生命周期中的持久性,因此只有在单个请求中多次重用一个变量时,它才有价值,这不是我的情况,但同时助手是模块,而不是类举个例子,现在我不知道该怎么办
如何缓存该散列,或以一种在请求中持续存在的方式对其进行记忆?如果要在启动时缓存某个痛苦操作的结果:
module MyExpensiveOperation
COMPUTED_RESULT = OtherModule.expensive_operation
def self.cached
COMPUTED_RESULT
end
end
只要确保模块以某种方式加载,否则它不会启动。如果需要,您可以在environment.rb
或作为config/initializer
类型文件强制-require
该模块
如果要延迟加载,基本原则相同:
module MyExpensiveOperation
def self.cached
return @cached if (defined?(@cached))
@cached = OtherModule.expensive_operation
end
end
这将处理由于任何原因返回
nil
或false
的操作。它将运行一次,并且仅运行一次,除非有多个线程同时触发它。如果是这种情况,可以通过自动锁定使模块并发性感知。根据您的注释,这只会在ap时更改应用程序启动,因此将其置于初始值设定项中即可
# config/initializers/expensive_thing.rb
$EXENSIVE_THING_GLOBAL = expensive_calculation
# or
EXPENSIVE_THING_CONSTANT = expensive_calculation
# or
Rails.application.config.expensive_thing = expensive_calcualatioin
模块中声明的常量应在模块使用或导入时随
include
一起使用的任何上下文中都可用。您想在启动时还是在第一次使用时使用它?您在这里描述的是视图缓存的确切用例。您能解释一下为什么不想使用Rails缓存吗?@Danielwestndorf-生成的散列有数百个键,它将用于应用程序的一个非常关键的部分,因此我希望它的性能尽可能好。例如,如果我使用memcached,散列将被序列化并保存为字符串,每次都需要对其进行解析(反序列化)请记住:这是一个固定的哈希,除非在部署之后,否则它不会改变,所以我不明白为什么我需要将本机ruby哈希序列化为字符串并保存到memcached中。@tadman我希望在启动时受到影响。从Memca来回传送这样的数据ched或Redis非常轻松。将其放入常量也应该可以。不清楚您的具体问题是什么。在第一个示例中,直接调用常量(MyExpensiveOperation::COMPUTED_RESULT
)与调用类方法(MyExpensiveOperation.cached
)有什么区别?我倾向于避免分享“内部信息”在类或模块之间,它会创建不必要的相互依赖关系。通过使用类似于“缓存”的方法,这意味着您可以更改“计算结果”的定义方式,以及它包含的内容,而不必破坏所有相关的代码。缓存的
可以更新以保持兼容性。常量没有这种能力为了适应,它要么是正确的格式,要么不是。