Rails Basics - before_save and items pending destruction
This is a short post to discuss a "gotcha" you might encounter when using the before_save event with nested models that may have destroy pending.
In this example, we have posts and comments (a basic blog setup). We have decided to store the number of comments for each post, inside a field in the post model, to save on calculation time. Comments are saved as nested attributes of the post, we are using the before_save event to calculate the total any time the post is saved. Our code for the model is:
class Post < ActiveRecord::Base
has_many :comments
accepts_nested_attributes_for :comments, :allow_destroy => true
before_save :calculate_total_comments
def calculate_total_comments
comments_total = 0
self.comments.each do |c|
comments_total += 1
end
self.total_comments = comments_total
end
end
The problem with this code, is that if a comment is marked for destruction (as happens when you flag it with the destroy parameter), it will still be counted when you save the post, as it has not been destroyed yet. The destruction occurs after the beforesave is complete.
To resolve this, we use the marked_for_destruction? method.
The improved code looks like this:
class Post < ActiveRecord::Base
has_many :comments
accepts_nested_attributes_for :comments, :allow_destroy => true
before_save :calculate_total_comments
def calculate_total_comments
comments_total = 0
self.comments.each do |c|
comments_total += 1 unless c.marked_for_destruction?
end
self.total_comments = comments_total
end
end