Rails 使用 gsub! 时发现的一个 ActiveModel 的问题

iwinux · 2012年03月03日 · 最后由 huacnlee 回复于 2012年03月04日 · 2858 次阅读

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

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,只是一般情况下比较难注意到这种问题 = =

注释写了 # If an attribute is modified in-place then make use of [attribute_name]_will_change! # to mark that the attribute is changing. Otherwise ActiveModel can't track changes to # in-place attributes. 如想做 in-place,向声明,然后在改内容。 没有你说的 Bug.你的代码并没有按约定去写。

attr_changed?应该是设置标志位来实现的,所以没有办法追踪到这种改动,如果去检查数据库来对比,那可能花销太大了,所以这也是不得以

确实,我以前也没有注意过,没这么用过,呵呵

匿名 #3 2012年03月03日

恩,不是 BUG,类似的问题很多,使用时要小心。

算是一个地雷了

这也是为什么 gsub 后面要加一个叹号的原因...你使用之前要清楚自己在干什么

这个陷阱挺危险的。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号