Ruby on Rails中方法内常量被意外修改的问题排查
问题分析与解决方案
问题根源
你遇到的核心问题是Ruby的Array#freeze是浅冻结:
- 你给外层数组
PROFILE_PARAMS调用了freeze,这只能阻止修改数组本身的结构(比如添加/删除元素、改变元素引用),但数组内部的子数组(每个["id","id"]这类元素)并没有被冻结,仍然是可变对象。 - 在
model_from_hash方法中,你调用了arr.shift——这个操作直接修改了PROFILE_PARAMS里的子数组元素,导致常量的内容被意外篡改。
具体解决方案
方案1:深度冻结常量(从根源阻止修改)
给PROFILE_PARAMS的每个子数组也加上freeze,实现深度冻结:
module ArrayTestMethods # 先给每个子数组冻结,再冻结外层数组 PROFILE_PARAMS = [ ["id","id"],["customer_id","contactId"],["channelId","channelId"], ["identifierId","identifierId"],["platform","platform"],["identifier","identifier"], ["created","createdAt"],["updated","updatedAt"] ].map(&:freeze).freeze # 其余代码不变 end
这样任何尝试修改子数组的操作(比如arr.shift)都会直接抛出RuntimeError,从根源上避免意外修改常量。
方案2:修改逻辑,避免破坏性操作
放弃使用shift这类修改原数组的方法,直接通过索引读取需要的内容:
def model_from_hash(params, array_map, debug = false) puts "array map before" pp array_map attr_hash = Hash.new array_map.each do | arr| model_attribute = arr[0] # 直接取arr[1..-1]作为参数,不修改原数组 data = json_walker(params, *arr[1..-1]) puts "arr0 - #{model_attribute} data - #{data}" if debug end puts "array map after " pp array_map return array_map end
这种方式不需要拷贝数组,性能更优,同时完全不会影响原常量。
方案3:创建数组副本操作(兼容原有逻辑)
如果必须保留shift这类操作,在方法内部创建原数组的深拷贝,操作副本而非原常量:
def model_from_hash(params, array_map, debug = false) # 给每个子数组创建副本,避免修改原常量 attributes_array = array_map.map(&:dup) puts "array map before" pp attributes_array attr_hash = Hash.new attributes_array.each do | arr| model_attribute = arr.shift data = json_walker(params, *arr) puts "arr0 - #{model_attribute} data - #{data}" if debug end puts "array map after " pp array_map return array_map end
内容的提问来源于stack exchange,提问作者Qd1y
相关产品推荐
相关产品推荐

