ruby 常量
多年前,我正在开发一个非常大的Ruby on Rails代码库,该代码库使用常量保存信用卡交易状态列表。 例如:
# example 1 class Txn ACTIONABLE_STATES = [:authenticated, :to_settle] DONE_STATES = [:settled, :declined]
# ... end
但是,我们有一个错误,已结算的交易可以通过txn.state.in? ACTIONABLE_STATES检查。 经过数小时的搜索,我们在代码库的完全不同的部分中找到了有问题的代码。
# example 2 all_states = ACTIONABLE_STATES.concat(DONE_STATES) all_states.each do |state| # ... end
如果您已经了解Ruby Array类,那么您可能已经发现了违规行。 #concat会更改原始列表,即使它不是以惯用的!结尾,并且由于ACTIONABLE_STATES静态保存在内存中,因此当执行此代码时,它将更改ACTIONABLE_STATES状态,直至该Ruby虚拟机的剩余寿命。
换句话说,在执行示例2之后,ACTIONABLE_STATES变为[:authenticated,:to_settle,:settled,:declined],直到重新启动服务器为止。
如果您在负载均衡器后面运行着多个Ruby框(就像我们所做的那样),则这可能会进一步混淆问题。 因为示例2可能只在某些特定的框上运行,所以您可能会遇到这样的情况,一个请求不会显示错误,而另一个请求会显示错误。
解决方案? 冻结您的常数。
# example 3 ACTIONABLE_STATES = [:authenticated, :to_settle].freeze DONE_STATES = [:settled, :declined].freeze
这将导致示例2产生无法修改的冻结数组(RuntimeError),并且如果您已经对示例2进行了单元测试,那么您甚至有可能在部署之前就捕获了它。
翻译自: https://hackernoon.com/freeze-your-constants-in-ruby-49e3238c19ef
ruby 常量