ActiveRecord Hangs On to Destroyed Relations
Rails' ActiveRecord has a nifty
#destroy method that almost everyone already knows about. The documentation for
#destroy1 helpfully states
Deletes the record in the database and freezes this instance to reflect that no changes should be made (since they can’t be persisted).
What is less clear is how it works when you call it on a relationship. To be sure, it destroyed the database record. What it also unfortunately does it hangs on to the reference in the parent object.
Let's throw down with some sample code.
class Thing < ActiveRecord::Base has_one :child end class Child < ActiveRecord::Base belongs_to :thing end zengarden> thing = Thing.new => #<Thing id: nil, name: nil, created_at: nil, updated_at: nil> zengarden> thing.save => true zengarden> thing.create_child :name => "Child" => #<Child id: 4, name: "Child", thing_id: 1,...> zengarden> thing.child => #<Child id: 4, name: "Child", thing_id: 1...> zengarden> thing.child.destroy => #<Child id: 4, name: "Child", thing_id: 1...> zengarden> thing.child => #<Child id: 4, name: "Child", thing_id: 1...>
Arg. I just destroyed it! I don't want it hanging around in my cache. If I really wanted to save it, I should have saved it before or during the destroy call. I can of course invalidate my cache by calling
which correctly returns nil. But if I don't know to do that immediately after the destroy, I get errors like
TypeError: can't modify frozen hash
because code used later naïvely calls
if thing.child thing.child.attributes = new_attributes end
expecting that if a child exists, it should be updated.
For the sake of argument, let's say that maybe the behavior isn't so bad, maybe this is what people expect or could use. In that case, what a terrible error message. I'm not familiar enough with the Associations internals to suggest where a better one could be injected, but something along the lines of
Can't modify a destroyed record
would be miles better.