accepts_nested_attributes_for でupdateされないパターン

ちょっとハマったのでメモ。

class User < ActiveRecord::Base
  attr_accessible :name, :profile_attributes
  has_one :profile
  accepts_nested_attributes_for :profile
end

class Profile < ActiveRecord::Base
  attr_accessible :name, :user_id
  belongs_to :user
end

こんなモデルがあったとき、Userを更新したりするとProfileも一緒に更新してくれる。
が、更新しないときもある。

Userのみロードしてたとき

user = User.where(:id => 123).first
user.name = 'hoge'
user.save! #=> Profileのsaveは走らない

UserからProfileを参照した後

user = User.find(123)
name = user.profile.name
user.name = name
user.save! #=> Profileのsaveが走る

ProfileからUserをとって更新したとき

profile = Profile.find_by_user_id(123)
user = profile.user
user.name = 'aaa'
user.save! #=> Profileのsaveは走らない

つまり、accepts_nested_attributes_for宣言しているモデルが対象のモデルへの参照を握っている場合は一緒にsaveが走るっぽくて、逆の参照持ってても走らないっぽい。まあsaveの主体がどっちかってのを考えれば自然か。
たぶんacriverecordのattribute_will_changeに追加されるとこのコードをもっと読めばはっきりしそう。
うっかりすると、accepts_nested_attributes_for対象のvalidationに引っかかったりするトラップが待っている。