Hacking Limbo

Reading / Coding / Hacking

ActiveModel 的一个诡异问题

直接上代码大家应该就明白了:

p = Post.first

content = p.content # 假设 p.content 的值是 "abc123"
content.gsub!('123', '456')

puts p.content # => 'abc456'
puts p.content_changed? # => false

p.content = content
p.save # => true # 没有出错
p.reload
puts p.content # => 'abc123' # 但是也没有保存成功

我的理解:

  1. 执行 content = p.content 这一句之后,content 跟 p.content 指向的是同一块内存
  2. gsub! 修改的是原内存位置的内容(不一定要是 gsub!,像 upcase! 这些方法效果也一样)
  3. 根据 ActiveModel::Dirty 的注释 可知,这种 in-place 的改动,ActiveModel 是无法追踪到的
  4. 只有当 p.content_changed?truep.save 才会保存新的 content 值。

严格来说不能算是 bug,只是一般情况下比较难注意到这种问题 = =