I ran into what a friend described as a corner case in ActiveRecord this afternoon, and it was a doozy for me to reason out. It has to do with the ever-so-convenient has_many :dependent => :destroy association option which allows you to cascade destroys down your object hierarchy, keeping everything neat and tidy.
As the has_many documentation states, this option should result in all the associated objects being destroyed alongside the original object by calling their destroy method. The problem lies in taking one associated object, and associating it with a different object, and then destroying the original object. One might expect that the original object will destroy only objects currently associated with it, but what it ACTUALLY does is destroy all the objects that were associated with it initially.
In practice this means that you can switch what object a record is associated with, destroy the original object, and the formerly-associated object gets destroyed too! The original object may not even appear to have a reference to the associated object at all -- but it will manage to destroy it nevertheless. Assuming you maintain a reference to the associated object, attempting to save it after it is destroyed will still succeed (!!!), but isn't helpful at all since it is merely updating a record that no longer exists in the database.
In any case, the way to work around this counter-intuitive behavior is to move your association to the new owner, save it, and then reload the original object before destroying it. Not obvious, and not pretty, but it was the easiest solution I could find in a pinch.
PS - I know that for trivial objects, it's far easier to clone the associated record and save it anew with its new owner, but that strategy is lamesauce for objects with deep hierarchy, as the clone method is only a shallow copy.
Have you filed a bug about this on Rails's lighthouse page? This sounds like a pretty serious case of something biting you in the ass unexpectedly.
Posted by: pete | 2010.08.07 at 12:28
Not yet. I have a test case associated with it that is tied to my application - I'm going to extract a self-contained test case sometime in the next week hopefully, and then I'll submit a bug with that included. I feel it's a bit difficult to explain otherwise (although I did an okay job in this post, if I may say so myself)
Posted by: Soulcutter | 2010.08.07 at 12:44