Preventing destory when grandchildren records exist?

OK, I'm hoping there's a better way to do this, but searching returns many more old hits than "current" ones.

I am working on an application that has Products belong to a Category and categories belong to a Group. I want to restrict the removal of a Category when it still has Products. I also want to restrict the removal of a Group if any of it's categories can't be removed.

class Group < ActiveRecord::Base    before_destroy :ensure_no_categories_with_products    has_many :categories, :dependent => :destroy

   def ensure_no_categories_with_products      unless self.categories.all? {|c| c.products.empty?}        self.errors.add_to_base "Can't destroy a group referenced by a category having products"        return false      end    end end

class Category < ActiveRecord::Base    belongs_to :group    before_destroy :ensure_no_products    has_many :products

   def ensure_no_products      unless self.products.empty?        self.errors.add_to_base "Can't destroy a category having products"        return false      end      # raise "Can't destroy a category having products" unless self.products.empty?    end end

class Product < ActiveRecord::Base    belongs_to :category end

As a side note, I couldn't find definitive guidance on whether returning false or raising an exception was "better", but returning false ended up working out better for my controllers.

So here's the big question: It feels wrong to duplicate the condition "unless self.categories.all? {|c| c.products.empty?}" in the Group model, but I'm not sure changing it to "unless self.categories.all? {|c| c.ensure_no_products }" feels any better.

I realize it's a bit asymmetric letting a Group be removed if there are existing Categories that happen to be Productless, but I'm OK with the :dependent => :destroy taking care of that.

What I'd love to know is if there's a better way. I can see this same kind of relationship other places:    Group=>Category=>Product    Author=>Post=>Comment    House=>Room=>Furniture

I found a thread on the Rails-core list (http://wrath.rubyonrails.org/pipermail/rails-core/2006-July/001986.html) that offered a similar solution via a new :dependent=>:restrict option on has_many (to mimic the RESTRICT option in some SQL dialects such as MySql http://dev.mysql.com/doc/refman/5.1/en/innodb-foreign-key-constraints.html), but I don't know if that led to this ticket (http://dev.rubyonrails.org/ticket/3837) which has a patch which is currently available as a plugin. (These are concerned with just the one-level dependency however.)

-Rob

Rob Biedenharn http://agileconsultingllc.com Rob@AgileConsultingLLC.com